= C notes == Cosmopolitan C compiler build-once run-anywhere: https://github.com/jart/cosmopolitan == Compilation with GCC recommendable call: gcc -Wall -Wextra -Wnull-dereference -Wsuggest-attribute=const -flto -fanalyzer -Wfloat-equal -Wduplicated-branches -Wduplicated-cond -fsanitize -Wlogical-op -Wrestrict -Wuseless-cast -Wjump-misses-init -Wdouble-promotion -Wshadow -Wformat=2 order of options: gcc [-c|-S|-E] [-std=standard] [-g] [-Olevel] [-Wwarn...] [-Idir...] [-Ldir...] [-Dmacro[=defn]...] [-foption...] [-mmachine-option...] [-o outfile] [@file] infile... === Generate shared object $ gcc -c -o ⇒ output.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped object file for shared library? -fPIC required! $ gcc -c -fPIC -o ⇒ (same “file” output) === compile a static C library $ ar rcs {1,…} ⇒ output.a: current ar archive === link statically Assume “bin/static/libr2d2.a” exists $ gcc -Lbin/static -lr2d2 -o ⇒ static-executable: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bc8a2e214e1c84d651ffb90469c8fa5c37cc011a, for GNU/Linux 3.2.0, not stripped ⇒ ldd static-executable linux-vdso.so.1 (0x00007fff235fd000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2cb4249000) /lib64/ld-linux-x86-64.so.2 (0x00007f2cb445e000) === create a shared library $ gcc -shared {1,…} -o ⇒ output.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=944df298d03a0e1ec63593d9b83633d0356fe647, not stripped === link dynamically with shared library Assume “bin/static/libr2d2.a” exists $ gcc -Lbin/shared -lr2d2 -o ⇒ shared-lib: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=96d63388acc6f8406a4124b3c51af299bceca36e, for GNU/Linux 3.2.0, not stripped ⇒ ldd static-executable linux-vdso.so.1 (0x00007fff235fd000) libr2d2.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2cb4249000) /lib64/ld-linux-x86-64.so.2 (0x00007f2cb445e000) === dynamic loading … with dlopen("libr2d2.so", RTLD_LAZY | RTLD_GLOBAL); … with dlsym(dlopen_handle, char* symbolname); … requires linking with -ldl set env LD_DEBUG to “lib”, “files”, “symbols” or “reloc” for more debugging information === run executable dependent on shared library $ set-env LD_LIBRARY_PATH $ ./ === More tools objdump -d | sed -n '//,/^$/p' nm readelf --relocs === Selected options -E Stop after the preprocessing stage; do not run the compiler proper. -S Stop after the stage of compilation proper; do not assemble. -c Compile or assemble the source files, but do not link. == Language basics to remember === Primitive types unsigned char int short long float double === Functions void bar(); // C: variadic function, C++: variadic function void bar(void); // C: function without arguments, C++: variadic function === error handling Approach 1 (classic): 1. return int which indicates the error that happened (or 0 for succes) 2. after every call, handle those integers Approach 2: 1. return a struct representing the object which you will continue to work with, and a 'valid' member 2. after a call, optionally check the valid member whether an error occured and it suits your error path 3. each successive function only run if the 'valid' member of the input argument is true === struct initialization typedef struct data { float x, y; } data; data val = { .x = 1.0f, .y = 1.0f }; // OK: struct initialization (not C++) data var = { .x = 1.0f }; // OK: y = 0.0f is implicit (ZII = zero is initialization) var = { .y = 1.0f }; // ERROR: not *initialization* anymore! var = (data){ .y = 1.0f }; // OK: special notation to enable this #define data_zero (data) { 3, 1 } // OK: best practice initial value (C-only), use with "data d = data_zero;" #ifdef __cplusplus #define literal(T) T #else #define literal(T) T #endif #define initial_value literal(data) { 3, 1 } typedef struct nested { data d; } nested; nested n = {{ d.x = 2.0, d.y = 4.0 }}; // OK: nested C struct initialization (also indexing like [0] is possible) === packaging • write libraries freehosted (no OS call, • implement single header libraries • only one library header with *all* declarations • if LIBXY_IMPLEMENTATION is defined, then this header file also includes its implementation #include "libxy.h" // only the headers/declarations are provided #define LIBXY_IMPLEMENTATION #include "libxy.h" // headers/declarations and implementation is provided == From newer standards === since C11: _Generic #define min(a, b) _Generic((a), float: min_for_float(a, b), int: min_for_int(a, b)) === since C11: static_assert #include int main(void) { static_assert(1 + 2 == 3, "your math is wrong"); } == Software/platform basics to remember === endianness on Intel CPU 4 bytes as first element of uint32_t array. Cast to uint8_t array. [0] is least-significant byte. [3] is most-significant byte. 4 bytes in uint8_t array. Cast to uint16_t array. [0] becomes least-significant byte of [0]. [1] becomes most-significant byte of [0]. #include #include int main() { uint32_t x[2] = {0x12345678, 0x0}; uint8_t *y = (uint8_t*) x; printf("[0] = %02X\n", y[0]); // [0] = 78 printf("[1] = %02X\n", y[1]); // [1] = 56 printf("[2] = %02X\n", y[2]); // [2] = 34 printf("[3] = %02X\n", y[3]); // [3] = 12 return 0; } #include #include int main() { uint8_t x[4] = {0x12, 0x34, 0x56, 0x78}; uint16_t *y = (uint16_t*) x; printf("[0] = %04X\n", y[0]); // [0] = 3412 printf("[1] = %04X\n", y[1]); // [1] = 7856 return 0; } === cmp convention smaller.compare(greater) ⇒ -1 greater.compare(smaller) ⇒ 1 equal.compare(equal) ⇒ 0 === signals (“man 7 signal”) Term Default action is to terminate the process. Ign Default action is to ignore the signal. Core Default action is to terminate the process and dump core (see core(5)). Stop Default action is to stop the process. Signal Standard Action Comment ──────────────────────────────────────────────────────────────────────── SIGABRT P1990 Core Abort signal from abort(3) SIGCHLD P1990 Ign Child stopped or terminated SIGFPE P1990 Core Floating-point exception SIGHUP P1990 Term Hangup detected on controlling terminal or death of controlling process SIGILL P1990 Core Illegal Instruction SIGINT P1990 Term Interrupt from keyboard SIGIOT - Core IOT trap. A synonym for SIGABRT SIGKILL P1990 Term Kill signal SIGQUIT P1990 Core Quit from keyboard SIGSEGV P1990 Core Invalid memory reference SIGSTOP P1990 Stop Stop process SIGTSTP P1990 Stop Stop typed at terminal SIGTERM P1990 Term Termination signal SIGXCPU P2001 Core CPU time limit exceeded (4.2BSD); see setrlimit(2) SIGXFSZ P2001 Core File size limit exceeded (4.2BSD); see setrlimit(2) low to high severity: [19 18 10 15 9] /* ISO C99 signals. */ #define SIGINT 2 /* Interactive attention signal. */ #define SIGILL 4 /* Illegal instruction. */ #define SIGABRT 6 /* Abnormal termination. */ #define SIGFPE 8 /* Erroneous arithmetic operation. */ #define SIGSEGV 11 /* Invalid access to storage. */ #define SIGTERM 15 /* Termination request. */ /* Historical signals specified by POSIX. */ #define SIGHUP 1 /* Hangup. */ #define SIGQUIT 3 /* Quit. */ #define SIGTRAP 5 /* Trace/breakpoint trap. */ #define SIGKILL 9 /* Killed. */ #define SIGBUS 10 /* Bus error. */ #define SIGSYS 12 /* Bad system call. */ #define SIGPIPE 13 /* Broken pipe. */ #define SIGALRM 14 /* Alarm clock. */ /* New(er) POSIX signals (1003.1-2008, 1003.1-2013). */ #define SIGURG 16 /* Urgent data is available at a socket. */ #define SIGSTOP 17 /* Stop, unblockable. */ #define SIGTSTP 18 /* Keyboard stop. */ #define SIGCONT 19 /* Continue. */ #define SIGCHLD 20 /* Child terminated or stopped. */ #define SIGTTIN 21 /* Background read from control terminal. */ #define SIGTTOU 22 /* Background write to control terminal. */ #define SIGPOLL 23 /* Pollable event occurred (System V). */ #define SIGXCPU 24 /* CPU time limit exceeded. */ #define SIGXFSZ 25 /* File size limit exceeded. */ #define SIGVTALRM 26 /* Virtual timer expired. */ #define SIGPROF 27 /* Profiling timer expired. */ #define SIGUSR1 30 /* User-defined signal 1. */ #define SIGUSR2 31 /* User-defined signal 2. */ == Snippets === Dump array in big-endian notation // substitute "T" for any type template void dump_array_hex(const char *name, T *arr, unsigned int len) { printf("# array %s of length %u with elements of %lu bytes\n", name, len, sizeof(T)); printf("%s = ", name); for (unsigned int i = 0; i < len; i++) { for (unsigned int j = 0; j < sizeof(T); j++) printf("%02X", (arr[i] >> (8 * (sizeof(T) - 1 - j))) & 0xFF); } printf("\n"); } // example: dump_array_hex("g", (uint16_t*)g, SYS_T + 1); === Generate a pseudo-hash of an array // substitute "T" for any type template T array_hash(const T *arr, int len) { T result = arr[0]; for (int i = 1; i < len; i++) { result ^= (arr[i] + arr[i - 1]); } return result; } // example: array_hash(pk, PK_NROWS*PK_ROW_BYTES) === union aliases typedef union mathvec2 { struct { float x, y; } struct { float u, v; } struct { float Left, Right; } struct { float Width, Height; } float Elements[2]; } mathvec2; === defer macro #include // create variable name specific for the call #define local_var(name) name ## __LINE__ // defer statement as a for-loop header with one iteration #define defer(start, end) for ( \ int local_var(_i_) = (start, 0); \ !local_var(_i_); \ (local_var(_i_) += 1), end \ ) void begin(void) { printf("begin\n"); } void end(void) { printf("end\n"); } int main() { defer(begin(), end()) { printf( "Hello World!\n"); } // output: "begin\nHello World!\nend\n" return 0; } === array API // implementation example: https://github.com/nothings/stb/blob/master/stb_ds.h // incomplete implementation #define dynarr(T) T* typedef struct dyarr_data { isize_t count; isize_t capacity; isize_t element_size; } dyarr_data; #define dynarr_init(T, init_size) malloc(…) … #define dynarr_add(arr, ...) dynarr_ensure_capacity(arr); (*arr)[dynarr_size(*arr)] = __VA_ARGS__ ; // API dynarr(int) arr = dynarr_init(int, 10); dynarr_add(&arr, 99); === basic interface for a custom allocator typedef struct allocator_t { void* user_data; void* (*proc)(allocator_t* this_allocator, isize_t amount_to_alloc, void* ptr_to_free); } === C 23 via https://lemire.me/blog/2024/01/21/c23-a-slightly-better-c/ // Only include stdio.h if it exists #if __has_include () #include #endif #include [[deprecated]] void f() {} [[nodiscard]] int g(int x) { return x + 1; } int main() { f(); // compile-time warning: 'f' is deprecated g(1); // compile-time warning auto x = 0b1111; typeof(x) y = 1'000'000; // type of y is the same as x printf("%d\n", x); // prints 15 printf("%d\n", y); // prints 1000000 constexpr int N = 10; // compile-time asserts using static_assert static_assert (N == 10, "N must be 10"); bool a[N]; // array of N booleans for (int i = 0; i < N; ++i) { a[i] = true; } printf("%d\n", a[0]); // prints 1 }