C is my life, here I try to have good time with C. Most of these examples using the Linux system calls and demonstrates operating system concepts.
Makefile is useful to building C-based project. The following example
shows a Makefile for building project that has main.c, gcd.c and lcm.c.
The functions.h contains the required definitions.
all: calc_lcm_gcd
calc_lcm_gcd: main.o gcd.o lcm.o
$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
%.o: %.c functions.h
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
rm main.o gcd.o lcm.o calc_lcm_gcdCheck here to read more.
For the smallest, single-file examples a full Makefile is more ceremony than
the code needs, so those directories use a justfile
instead — a plain list of named recipes with no dependency graph to maintain.
default:
@just --list
build:
cc -Wall -O0 -g -std=gnu11 main.c -o example
run: build
./example
clean:
rm -f exampleExamples that pull in a real external dependency (GLib, Wayland, ...) use
Meson instead, since dependency() resolves the
library via pkg-config without hand-rolling CFLAGS/LDLIBS:
project('example', 'c')
glib = dependency('glib-2.0')
executable('example', 'main.c', dependencies: [glib])Directories with more than one translation unit, or that need explicit link
flags like -lm, still get a Makefile — make's dependency graph is the
right tool once a build has more than one moving part.
Linux provides the ability to create threads using the clone() system call.
When clone() is invoked, it is passed a set of flags that determine how much
sharing is to take place between the parent and child tasks.
Let's learn to use new feature in C11, C11 threads. Please see here as a reference for C11 threads.
But at the current time nobody implements this feature.
Generic selection is implemented with a new keyword: _Generic.
The syntax is similar to a simple switch statement for types:
_Generic( 'a', char: 1, int: 2, long: 3, default: 0)The above expression evaluates to 2 (character constants are ints in C).
The use of _Generic can be abstracted in a macro:
#define type_idx(T) _Generic( (T), char: 1, int: 2, long: 3, default: 0)So that type_idx('a') evaluates to 2 and type_idx("a") evaluates to 0.
Ahmad Asadi, had guess about floating point number implementation in C:
Let's check it.
The alloca() function allocates size bytes of space in the stack
frame of the caller. This temporary space is automatically freed
when the function that called alloca() returns to its caller.
#include <alloca.h>
void *alloca(size_t size);Learn about fork system call with its related system calls like wait.
This program shows that the expressions with side effects has no side effect in an unevaluted context.
sizeof(a++)A minimal libglib-based "hello world", built through a real CMakeLists.txt
(with a src/ subdirectory) instead of a Makefile. Run git clean -f -d -X
to remove the files CMake generates.
Reads a line from stdin, finds occurrences of a couple of known words inside
it, appends each match to test.txt, and prints a final timestamp to the
console.
A grab-bag of tiny C snippets (globals vs. bss/data, for/if control
flow, function calls, inline __asm__, section attributes, argument counts,
float promotion) meant to be compiled to assembly and read side by side with
the C, one concept per file.
Generate random number between 0 to 3 with random instead of rand.
Review on pointer concepts and some common mistakes about them.
A twin prime is a prime number that is either 2 less or 2 more than another prime number—for example, either member of the twin prime pair (41, 43).
Convert a double number into byte representation.
Slices are a Golang concept but here we tried to implement it in C and check how is the idea working.
There is a first time for everything, this is the first time that I saw an ARMv6.
Using pipe to communicate between parent and child processes.
The root-level main.c (built via the top-level CMakeLists.txt or just build)
reads lines from a text file, reverses each word and wraps it with A/a, and
writes the result to another file. Update the hardcoded input/output paths at the
top of main() before running it.
A hand-rolled implementation of asprintf(), which allocates a string
buffer of exactly the right size for a formatted print instead of forcing
the caller to guess a buffer size up front.
Bit-field struct members (unsigned char p1 : 1) and how they change the
overall size of a struct compared to using full-width members.
An early, unfinished blackjack game: deals cards to the player and dealer and lets the player hit. Left here as-is, bugs included.
Reads a FILE*'s internal stdio buffer size with the glibc extension
__fbufsize().
Writes a fixed-size number to a file and reads it back with fwrite() /
fread(), using fseek() and ftell() to check its position and size.
Finds the maximum of two doubles and compares two numbers without a single
if statement, using fabs() and integer arithmetic tricks instead.
Authenticates a username/password pair against the system's password and
shadow databases using getpwnam(), getspnam() and crypt(), then
overwrites the plaintext password in memory as soon as it's been hashed.
Converts a decimal number to binary by pushing its bits onto a hand-built linked-list stack and popping them back off in order.
Numeric differentiation using the symmetric-difference formula, computing both the first and second derivative of a function passed in as a function pointer.
Recursively walks a directory tree, using C's designated-initializer
default arguments to call process_dir() with just the arguments that
matter.
A single integer division by zero, to observe how this platform actually handles it.
Reads a C function body typed at runtime, compiles it into a shared object
with gcc, and calls it immediately via dlopen() / dlsym().
Emulates Go-style (value, error) return tuples in C with a macro that
generates a small {value, error} struct per type.
Compares the different ways a C program can terminate — exit(),
_Exit(), quick_exit() — and which cleanup handlers each one runs
(atexit() vs at_quick_exit()).
Reads raw fields (signature, filesystem state, OS id, last-mount and last-written timestamps) directly out of an ext2 superblock on a block device.
Nibble-level arithmetic building blocks (a half-multiplier and half-adder over a 4-bit finite field), the kind of primitive used to build an AES-style S-box.
Recursive Fibonacci next to an iterative version that keeps its running
state in static locals.
Pokes at glibc's private FILE struct fields (_r, _w) to see how stdio
tracks its read/write buffering internally.
A small "fat string" library — fstr_new(), fstr_cpy(), fstr_free() —
implementing reference-counted substrings over a shared backing buffer.
Using GLib's doubly linked list, GList, and a demonstration that it only
stores pointers: mutating a local array after appending it changes what
the node holding that pointer prints, while nodes appended with string
literals are unaffected.
A single function that assigns to an extern int i never defined in this
translation unit, built with gcc -nostdlib -shared -fPIC into got.so —
inspect the resulting shared object (e.g. objdump -R got.so) to see the
GOT entry created for the unresolved external write.
Reads an N x N grid size and contents from a text file into a dynamically
allocated 2D array.
Making an HTTP request with GNOME's libsoup, using SoupSession and
SoupMessage.
A libsoup-based HTTP server that proxies another response back to the
client, copying headers across messages.
Implements a custom GLib GSource (MessageQueueSource) that dispatches
whenever a message becomes available on a GAsyncQueue.
scanf() format specifiers, in particular the %[...] scanset conversion,
and how easily it overflows a fixed-size buffer.
A minimal Java Native Interface example: a native method implemented in C and called from Java.
Reads lines of input with the long-removed, inherently unsafe gets() on
purpose, as a cautionary example. The buffer-growth helper is declared but
never actually wired up, so despite the bookkeeping this is capped at 16
lines, not unbounded.
A singly linked list built around a generic linked_list_append() macro
that works over any struct with a next pointer.
Nonlocal control flow with setjmp() / longjmp(), jumping back into two
independent call sites through two separate jump buffers.
Peeks just behind a pointer returned by malloc() to read glibc's internal
chunk-size header.
Maps a file into memory read-only with mmap() and dumps its contents
byte by byte.
Sorts an array of struct date records ascending or descending with
qsort() and a pair of custom comparators.
A minimal shell: reads a command line, tokenizes it on whitespace, and runs it.
Builds a multiplication table with a GSL matrix (via libapop), scaling
each row and column by its index.
Switches stdin to O_NONBLOCK with fcntl() and shows how fgets()
fails immediately when there's nothing to read, instead of blocking.
An OpenMP #pragma omp parallel for loop, wrapped in a small pfor()
macro.
OpenMP #pragma omp parallel sections, where each section body runs on
its own thread.
Preprocessor tricks: stringizing an expression to print it alongside its
value (Peval), and generating an array declaration via token pasting
(ListGen).
Reads the calling process's CPU affinity mask with sched_getaffinity().
Queries a pthread attribute object for its contention scope and scheduling policy.
A French-language console CRUD app over a dynamic array of employee records: add, list, search, delete, and sort by name or age.
A POSIX shared-memory producer/consumer pair (shm_open() + mmap()) for
basic inter-process communication.
A minimal single-client TCP server that binds, listens, and accepts a connection.
A grab-bag of small, standalone programs originally written as answers to Stack Overflow questions — dynamic 2D array allocation, fork + pipe + sort, and similar.
Recursively generates every subset of size k from a fixed set.
Prints the address of an extern global to inspect how symbol resolution
works across translation units.
A TCP echo client: connects to a server, sends a word, and prints whatever comes back.
The typeof compiler extension, used to declare a pointer whose type is
derived from another variable's type instead of being spelled out.
Allocates a 5-int array, zeroes it, and marks the last slot with a -1
sentinel that getSize() walks the array to find. Despite the directory's
name, this particular walk stays within bounds and frees cleanly — nothing
here actually triggers a Valgrind complaint as written.
Connects to a Wayland display and prints the protocol version — the "hello world" of Wayland clients.
A wc-like line reader that grows a 2D char array on the fly as it reads
stdin one character at a time.
A who-like utility built on GLib hash tables, grouping logged-in users
(pulled from utmpx) by user id.