This is a compilation of the C language programming tips I posted on the Fediverse during 2022:
The elvis operator
https://triptico.com/social/angel/p/1673265846.832608If you program in C, you've probably used what is called the ternary operator a million times in expressions like this:
value = user_value ? user_value : default_value;
Where you test user_value
and, if it's non-zero, you store it in value
; otherwise, you set value
to default_value
.
What you may not know is that, thanks to a gcc (and others) extension, you can abridge that expression to this:
value = user_value ?: default_value;
This ?: thing is colloquially named the 'Elvis' operator (if you don't see why, just look at it with you head slightly slanted to the left).
https://en.wikipedia.org/wiki/Elvis_operatorLinker optimization
https://triptico.com/social/angel/p/1672111113.792792TIL that there is something named linker optimization that forces the deletion of unused functions in your final executable. As a bonus, you get the list of unused functions as compiler warnings (dead code is always bad, so you get the change to delete them or comment them out).
If you have a standard build system, do the following:
make CFLAGS="-ffunction-sections -fdata-sections" LDFLAGS="-Wl,--gc-sections -Wl,--print-gc-sections"
Concatenated PPM images as a video stream
https://triptico.com/social/angel/p/1671347271.960902TIL that most video processing tools accept as a valid input stream a set of concatenated PPM [1] files, so you can generate video from your own programs easily. You just have to pipe your program's output to the appropriate tools (indicating the FPS) like
./fancy-prg | mpv --no-correct-pts --fps=$FPS -
or
./fancy-prg | ffmpeg -i - -r $FPS video.mp4[1] Netpbm format (Wikipedia)
Full paths in shared libraries' soname
https://triptico.com/social/angel/p/1667714202.685173TIL that you can have a full-path soname
in a dynamic library, so that binaries linked to it can find it even if it's in a non-standard place:
gcc -shared -o libcrazy.so -Wl,-soname,/an/esoteric/place/to/store/libcrazy.so crazy.c gcc -o main -L. -lcrazy main.c
This way, you don't need kludges regarding the use of LD_LIBRARY_PATH
nor anything else.
Executing hand-crafted machine code
https://triptico.com/social/angel/p/1667464895.428412If you want to create an executable program in memory and execute it without going through the filesystem, this is how you do it (Linux only):
int fd = memfd_create("foo", MFD_CLOEXEC); // write your image to fd however you want fexecve(fd, argv, envp);https://unix.stackexchange.com/questions/230472/can-i-exec-an-entirely-new-process-without-an-executable-file
Finding memory leaks
https://triptico.com/social/angel/p/1667328755.759676If you develop in C, you'll probably end up using memory leak detection tools like valgrind
, libleak
or the scan-build
tool from the LLVM compiler. There is a less known tool inside the GCC compiler itself: the -fsanitize=address
.
If you use a standard make setup, you can recompile your project doing
make CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
After exiting your program, a summary of memory leak errors (including the line of the source code were it happened) will be printed out.
Poor man's profiler
https://triptico.com/social/angel/p/1665694300.323309Poor man's profiler, or profiling a running program with a cheap combination of gdb, awk and scripting:
http://poormansprofiler.org/Sampling tools like oprofile or dtrace's profile provider don't really provide methods to see what [multithreaded] programs are blocking on - only where they spend CPU time. Though there exist advanced techniques (such as systemtap and dtrace call level probes), it is overkill to build upon that [...]
[...] one needs to improvise, like.. use debuggers - they can walk threads and provide stacks.
Fast character case conversion
https://triptico.com/social/angel/p/1666985574.734641Fast character case conversion (or how to really compress sparse arrays):
https://github.com/apankrat/notes/tree/master/fast-case-conversionConverting strings and characters between lower and upper cases is a very common need.
In particular, case conversion is often used to implement case-insensitive comparision, an operation that is often present on the program's fast paths as a part of data container lookups and content manipulation.
So it is usually desirable to make case conversions as fast as possible.
In this post we are going to look at one of the options - very fast case conversion using compressed lookup tables and also at some options for compressing these even further.
gdb surprises
https://triptico.com/social/angel/p/1667549267.231691TIL by accident that you can type make
from inside gdb and does what you think it does.
Modern C for C++ Peeps
https://triptico.com/social/angel/p/1673121099.281843I find the article interesting not only for C++ "peeps", but for us greybeard C programmers as well, who learnt the language in the eighties. Remarkable sections in the document are:
- Use struct wrappers for strong typing (I've never done this, but it's interesting; accessing the components inside the structs may become a bit tedious, though).
- Initialization in C99 (I haven't used this to its full potential).
- Don’t be afraid to pass and return structs by value (as an old fart, I always pass pointers to structs, and this section reasons otherwise for small structs).
- Named optional arguments (or, as the author more accurately describes this, the "option bag").