What is your favorite C programming trick? (2009)
stackoverflow.com
stackoverflow.com
struct calculate_args {
int x, y;
enum {add=0, sub} operator;
};
int calculate_func(struct calculate_args args) {
if (args.operator == add)
return args.x + args.y;
else
return args.x - args.y;
}
#define calculate(...) calculate_func((struct calculate_args){__VA_ARGS__})
Now you can combine positional and named parameters or omit them. calculate(1, 3); // 4
calculate(8, 3, .operator=sub); // 5
calculate(.operator=sub, .y=7); // -7
Works very well in cases where there is a lot of parameters that default to 0.
Keep in mind that you still need to know how structs work and you lose compile-time error detection.I just wanted to post it, but you already did. :-)
#include <stdio.h>
#define CONCAT2(a, b) a##b
#define CONCAT(a, b) CONCAT2(a, b)
#define COUNT_ARGS2(a0, a1, a2, a3, a4, a5, a6, a7, a8, N, ...) N
#define COUNT_ARGS(...) COUNT_ARGS2(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define foo(...) CONCAT(foo_, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__)
void foo_1(const char *a){
printf("one arg: %s\n", a);
}
void foo_2(int a, int b){
printf("two args: %i and %i\n", a, b);
}
int main(){
foo("asdf");
foo(314159, 271828);
}
Small gotcha: Does not work with zero parameters, but functions with zero parameters probably mess with global state and that's evil anyway. *outArg = myPtr; myPtr = NULL;
free(aPtr); aPtr = NULL;
Set your pointers to null when you free them! Set them to null when you transfer ownership! Stop leaving dangling pointers everywhere!Some people say they like dangling pointers because they want their program to crash if something is freed when they don't expect it to be. Good! Do this:
assert(ptr);
There are also many more tricks you can do once you start nulling pointers. You can use const to mark pointers that you don't own and thus can't free. You can check that buffers are all zero before you free them to catch memory leaks (this requires zeroing out other fields too of course).Please, null out your pointers and stop writing (most) use-after-free bugs!
The trick of checking that buffers are zeroed is purely a debugging tool, so it's okay if it doesn't work on some platforms. And if you allocate with calloc(), the padding will be zeroed for you. It's actually very rare that you will have to call memset() with this technique.
This is like the most clichéd way of saying “my code has security vulnerabilities” that there is. I have yet to see code that has remained secure solely on the “discipline” of programmers remembering to check things.
> The trick of checking that buffers are zeroed is purely a debugging tool, so it's okay if it doesn't work on some platforms.
Fair.
> And if you allocate with calloc(), the padding will be zeroed for you.
It might get unzeroed if you work with the memory.
Also, it's not just about vulns in security-critical code. It's also about ordinary bugs. Why not be a little more careful? It won't hurt.
> It might get unzeroed if you work with the memory.
Maybe, but it isn't very common. I'm not sure when the C standard allows changing padding bytes, but in practice the compilers I've used don't seem to do it. And again, it's just a debugging aid, if it causes too much trouble on some platform, just turn it off.
That's not what the parent comment said.
You retort: "I have yet to see code that has remained secure solely on the “discipline” of programmers remembering to check things."
I think that is a dishonest misrepresentation of what the parent comment said, isn't it?
Honestly, unless you really cannot afford it time-budget wise, I would just ship everything with ASAN, UBSAN, etc. and deal with the crash reports.
Left unsaid due to the fact I was out doing groceries this morning when I posted that was that I don't think this is even a very good practice in general, as I explained in more detail in other comments here.
You took the original argument, changed it to be fallacious, and used it as a strawman. That's what was dishonest about it.
Is that a real issue, though?
> padding is going to make your life annoying
Just memset?
> Is that a real issue, though?
Of course, it's not, but that's one of those factoids that everyone learns at some point and feels like needing to rub it into everyone else's face assuming that these poor schmucks are as oblivious to it as they once were. A circle of life and all that.
Do you use sanitizers for use-after free bugs? I see many people still don’t use them even though sanitizers have become very good in the last 5-6 years
I use every tool at my disposal. Sanitizers, static analyzers... and also not leaving dangling pointers in the first place. Why would I do anything less? It doesn't cost anything except a little effort.
Take a look at this recent HN link: https://www.radsix.com/dashboard1/ . Look at all those use-after-free bugs. Even if it only happens 1% or 0.01% of the time... It's a huge class of bugs in C code. Why not take such a simple step?
But I'll also say that if you don't have any use-after-free bugs in the history of a large C codebase... you might not even be on the lookout for them? I still have them sometimes, mainly when it comes to multiple ownership. And those are just the ones I found eventually.
So yes, different strokes for different folks, but if you make the effort to incorporate tricks like this into your "unconscious" coding style, the ongoing effort is pretty minimal. Even if you decide this trick isn't worth it, there are countless others that you might find worthwhile. I'm always on the lookout for better ways of doing things.
As an added benefit the code also becomes more robust in the production environment, if there are use cases you failed to consider -- 100% branch coverage does not guarantee that there are none!
Whole heartedly agree.
> If a program fails to compile or quickly crashes (because of a failed assertion), then I consider that better than having to unit test and fuzz test your code to find that particular problem.
This confuses me. My typical order would be:
fails to compile > unit test > quick crash at runtime > slow crash at runtime (fuzzing)
I am curious to understand why we differ there.
#define ZFREE(p) do { free(p); p = NULL; } while(0)
#define free(p) do { free(p); p = NULL; } while(0)
And when you want to call the original free: (free)(p);
The preprocessor will not substitute this occurence of free. if(x) MACRO();
else something();
expands to if(x) { ... }; // Error!
else something(); #define FREE(ptrptr) do { \
__typeof__(ptrptr) const __x = (ptrptr); \
free(*__x); *__x = NULL; \
} while(0)
There might be a better way of doing it though. Also, __typeof__() obviously isn't standard C.Edit to add: I've honestly been moving away from using a macro and just putting both statements on one line like in the OP. For something so simple, using a macro seems like overkill.
Maybe someone else could figure out how to do it properly, since I'd definitely prefer a function.
int* p = malloc(sizeof(int));
FREE(&p);
What if we instead define the macro like this: #define FREE(ptr) do { \
__typeof__(ptr)* const __x = &(ptr); \
free(*__x); *__x = NULL; \
} while(0)
Then make usage slightly shorter, as well as more similar to free(): int* p = malloc(sizeof(int));
FREE(p); #ifndef __clang__
#define assume(expr) \
__extension__ ({ \
static_assert(__builtin_choose_expr( \
__builtin_constant_p(expr), expr, true), #expr); \
assert(__builtin_choose_expr( \
__builtin_constant_p(expr), true, expr)); \
})
#else
#define assume(expr) \
__extension__ ({ \
static_assert(__builtin_constant_p(expr) ? (expr) : true, #expr); \
assert(__builtin_choose_expr( \
__builtin_constant_p(expr), true, expr)); \
})
#endif
Note that the clang semantics of language extensions sometimes differ from GCC.I also like to use __auto_type very much:
#define var __auto_type
#define let __auto_type const
As in: #define m_max(a, b) \
__extension__ ({ \
let _a = (a); \
let _b = (b); \
_a > _b ? _a : _b; \
})And in case anyone else is wondering, the advantage of that m_max macro is that it evaluates each of its arguments only once.
static struct {
int a;
float b;
const char* c;
struct {
float x, y, z;
} nested;
} state = {
.a = 1,
.b = 2.0f,
.c = "Hello World!",
.nested = {
.x = 1.0f, .z = 3.0f
},
};
Careful if the the struct contains big arrays though, this will let the executable size explode because a copy of the struct content is placed in the executable. static struct {
const char *key;
const char *value;
} elems[] = {
{ "k1", "v1" },
{ "k2", "v2" },
{ "k3", "v3" }
};
for (int i=0; i<sizeof(elems)/sizeof(*elems); i++) {
printf("%s => %s\n", elems[i].key, elems[i].value);
}https://blog.gboards.ca/2020/02/adventures-in-obscure-c-feat...
E.g. this is 'named, optional arguments East Egg' is a useful trick which also improves readability:
my_func((my_struct) { .bla = 1, .blub = 2, });The compatibility with the C library is up to C11 for example.
Google even sponsored a massive effort to clean the Linux kernel of their use.
A subset of designated initializers is supported in C++20.
Took them only 21 years... And this standard being so recent, most projects won't have that for years to come.
In your example,
Point point = {.z = 2.0, .x = 1.0};
produces a compilation error. This is annoying, but workable. And at least it's a compile-time error, rather than a bug that perniciously sneaks into production.It's interesting that the other C descendant Objective-C has decided to "respect" its C subset instead of messing with it, with the result that new C standards are automatically supported in ObjC.
VLAs are dead, a broken design fixed in C11 by removing it from the standard, no idea why everyone keeps referring to them.
typedef struct pixel {
int r;
int g;
int b;
} pixel_t;
#define PIXEL(red, green, blue) \
(pixel_t) \
{ \
.r = (red), \
.g = (green), \
.b = (blue)
}
Then I could invoke the function void display_pixel(pixel_t pixel);
by calling, display_pixel(PIXEL(255, 255, 255));
And you could even, return PIXEL(255, 255, 255);
Like magic. In other words, how do you pretend that you have syntax-level OOP in C... On retrospect, the macro could reduce readability, writing the argument explicitly may be better. display_pixel((pixel_t) {.r = 255, .g = 255, .b = 255}); > The best C programming trick is to use no tricks.
Yes! C's monotony is its killer feature. Take away power when you are coding and you have more power to share the code and debug it.The very notion of there being tricks and that knowing them makes you better is something I hate about C and C++. Most tricks I read here are bandaids over usability issues the languages have. Yes, they alleviate an issue but may introduce unexpected consequences and distance your dialect from the rest of the community.
I am so very thankful that C and C++ are no longer the only options for low level, non garbage collected programming.
Effectively generators a la python or javascript, implemented using a set of macros that expand to a Duff's device control structure.
#define crBegin(state) \
int *__state_ptr__ = &(state); \
*__state_ptr__ = 0; \
switch(*__state_ptr__) { case 0:
#define yield(x) \
do { \
*__state_ptr__ = __LINE__; \
return x; \
case __LINE__:; \
} while (0)
#define crFinish }
struct counter_frame {
int state;
int i;
};
int counter(struct counter_frame *frame) {
crBegin(frame->state);
frame->i = 0;
while (1) {
yield(frame->i++);
}
crFinish;
}
int main() {
struct counter_frame my_counter;
while (1) {
printf("%d\n", counter(&my_counter));
}
return 0;
} /* Return the offset of 'member' relative to the beginning of a struct type */
#define offsetof(type, member) ((size_t) (&((type*)0)->member))
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))Still interesting to see the trick behind it tho.
The way it's used is that Ganesha supports defining of alternate filesystem backends and serving them as NFS shares. Handles to objects (e.g. files) would exist as pointers which live inside the struct of the backend's handle struct. i.e.:
struct my_file_data {
struct ganesha_file_data {
// generic data
};
// data specific to my module
};
The "my" module would take pointers to ganesha_file_data when the NFS core code calls it. The "my" module then uses container_of to convert ganesha_file_data ptr to my_file_data ptr.This really helps whipping people into shape and making them pay attention to what they write. Also helps documenting the code in a very succinct way.
You can also print backtraces when an assertion fails: https://stackoverflow.com/questions/77005/how-to-automatical...
(Whether you should use assert for that kind of checks that probably only should run in tests is debatable, but it sometimes happens)
switch (...) {
case A:
...
case B:
...
...
default: // all possible cases are dealt with above
assert(0);
} assert(!"You gave an invalid letter");
The string literal is a pointer, so !pointer is false. And then you get a nice explanation message when the assertion fails. I wish all assert gave optional explanation messages.https://github.com/nothings/stb/blob/master/stretchy_buffer....
This basically allows you to use std::vector<T> like vectors in C, but with an added benefit that you can subscript the vector like arr[3] rather than using unwieldly functions like vector_get(arr, 3) or vector_put(arr, 3, value).
foo();
//*
some();
code();
which_you_want_to_comment_out();
// */
other_code();
Code between comments are 'enabled' for now, and can be commented out by simply removing a slash in the '//*'.A lot of the rest of these look like they’re just trying to be clever for the sake of it.
/** /
do_thing();
/*/
do_other();
//*/
which allows quickly toggling between `thing` and `other` while debugging.This is from APUE, 3rd Edition.
void (*signal(int signo, void (*func)(int)))(int);
vs typedef void Sigfunc(int);
Sigfunc *signal(int, Sigfunc *); __attribute__((cleanup(cleanup_function)))
It allows you to create, in essence, destructors for pure C code.1. https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Common-Variabl...
debug && printf("counter: %d\n", i);Example with 3 "values". This is the base definition from which all the tables and enums are produced.
#define FOREACH_LAN(LAN)\
LAN(GA, IE, C_ANSI ) /**< Irish Gaelic : 0 */ \
LAN(DE, DE, C_ANSI ) /**< German : 1 */ \
LAN(DA, DA, C_ANSI ) /**< Danish : 2 */ \
LAN(EL, EL, C_GREEK ) /**< Greek : 3 */ \
LAN(EN, GB, C_ANSI ) /**< English : 4 */ \
LAN(ES, ES, C_ANSI ) /**< Spanish : 5 */
etc.
Let's define an enum indexing with these languages #define GENERATE_LANIDX(lan,country,codepage) LANIDX_ ## lan,
typedef enum {
LANIDX_UNDEFINED = -1, //
FOREACH_LAN(GENERATE_LANIDX)
LANIDX_MAX // Automatically get the upper bound
} LANIDX_TYPE;
This is equivalent to typedef enum {
LANIDX_UNDEFINED = -1, //
LANIDX_GA,
LANIDX_DE,
LANIDX_DA,
LANIDX_EL,
LANIDX_EN,
LANIDX_ES,
LANIDX_MAX // Automatically get the upper bound
} LANIDX_TYPE;
but I didn't need to repeat all the language codes, the macro did it for us.Now in the module I can define tables, also without needing to repeat the codes
const char *LanIdx2LanTable[] = {
#define GENERATE_LANIDDX_2_LAN(lan,country,codepage) [1+ LANIDX_ ## lan]={#lan},
FOREACH_LAN(GENERATE_LANIDDX_2_LAN)
};
const char *LanIdx2CodePageTable[] = {
#define GENERATE_LANCP(lan,country,codepage) [1+LANIDX_ ## lan]=codepage,
FOREACH_LAN(GENERATE_LANCP)
};int x = 10;
while (x --> 0);
for(size_t i=N; i-- > 0 ;) do_stuff_with(&a[i]); if (turn_on() || reset(), turn_on()) { // try to turn it on. if fail, reset and try again
// it's on
}It's always going to try again, though, even if successful the first time. Perhaps you forgot parentheses?
if (turn_on() || (reset(), turn_on())) #ifdef _DEBUG
#define __REVIEW_ME / ## /
#else
#define __REVIEW_ME $review_me$
#endif
When reworking a large amount of code, this can be used to tag places that may need another pass or a review. The debug version will build fine, but the release won't until all review tags are removed.This also allows adding free form comments if needed:
foo += 13; __REVIEW_ME -- why 13 ?! typedef bool foo_fn(int, char);
This declares a type of a function. foo_fn foo1, foo2, foo3, foo4;
This declares 4 functions of the same type. It's equivalent to bool foo1(int, char);
bool foo2(int, char);
bool foo3(int, char);
bool foo4(int, char);
Unfortunately the type can not be used at function definition but that is not where function are interesting. They are neat for function pointers especially those that require casting. void function_taking_foo(int, foo_fn *);
function_taking_foo(1, foo1); // no cast necessary as type is identical and even
instead of function_taking_foo(1, (bool(*)(int,char))foo1);
When you have a lot function pointers it is incredibly more readable than the usual syntax.#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
2. Not strictly C: using `pahole` to analyze how structs are laid out in memory
My favorite "programming" trick only applies if I'm not sharing my code. I just forego header files entirely, and just #include the .c source files. Also, put `#pragma once` in all the .c files to avoid double-inclusion without the hassle of #if ... etc. This requires a bit more diligence since you can't have mutual recursion between source files.
struct foo { int a, b; };
A function that works on the structure looks like this, you use '->': void pr(struct foo *n) { printf("%d %d\n", n->a, n->b); }
But when you make an instance of this structure, you have to use this different syntax: struct foo p;
p.a = 1; // Yuck!
pr(&p); // Bad!
But you can do this instead: struct foo p[1];
p->a = 1; // Very nice..
pr(p); // Yes!Anyway it means you can't cut and paste code from one place to another without changing '.' to '->' or vice-versa.
Because using `(*ptr).member` everywhere is annoying. There's plenty of times you want or need to have direct access to a member rather than always dereferencing a pointer.
Here's a variation that seems plausible: make postfix p^ be like C's p[0], and infix p^i like C's p[i]. (With a tighter binding for ^ than C has.)
https://github.com/ekmett/lens/wiki/Examples
> ("hello","world")^._2
E.g. a:
int x = a->b->c->d;
means there's 3 memory accesses, while
int x = a.b.c.d;
means there's one memory access for the whole expression.
Also consider this:
int x = a->b.c->d;
I can immediately see where pointer indirections are happening.
...unless you're in C++ of course which messed up this simple rule when references were added to the language.
It's not true if the compiler can figure out that the left side is a constant, consider:
struct foo { int a; };
struct foo z = { 7 };
struct foo *const p = &z;
Then in z->a, no indirection is necessary. GCC -O2 makes this: int fred() { return p->a; }
fred:
movl z(%rip), %eax
ret
p:
.quad z
z:
.long 7A chain of '.' on the other hand is always guaranteed to be resolved into a single offset at compile time.
switch (fruit) {
case APPLE:
if (true) {
pick_apple();
} else
case BANANA:
{
pick_banana();
}
box_fruit();
}An example of where this matter is an AST forest, like this:
struct node { int tag; }; // Generic node
struct infixnode { int tag; struct node *l, *r; };
struct intnode { int tag; int val; };
But it's really ugly to use. If you have an expression represented in this AST like 'a(b(c+d))' and you want to access 'd', you need to do this: struct node *n;
int d = ((struct intnode *)(((struct infixnode *)(((struct infixnode *)(((struct infixnode *)(((struct infixnode *)n)->r))->r)))))->val;
But if you use unions: union node { struct infixnode infix; struct floatnode floatval; struct intnode intval; };
Then you can say this: union node *n;
int val = n->infix.r->infix.r->infix.r->intval.val;
The same holds for C++. In C++ you could make a class hierarchy for your AST. But you still have the casting to convert to the derived types, which is just as ugly.. But you can instead make inline access functions in the base class whose sole purpose is to do this casting, you end up something like this: node *n;
int val = n->infix()->r->infix()->r->infix()->r->intval()->val;Definitely not exhaustive as this thread though.
[1] https://ne02ptzero.me/blog/c-tricks-that-i-use-everyday.html
One might argue it’s not even C..
Also, anything involving arrays of pointers to functions is cool by definition.
People might like it or not, but SO is a tool that provides fast access to answers on technical questions because people want to solve their problems(and learn), it's QA.
If you want to discuss stuff then there are forums, reddits, discords or even HN.
struct foo star p = malloc(sizeof (struct foo) + 100); p->b[50] = 'x';
struct foo { int a; char *b; }
struct foo *p = malloc(sizeof (struct foo) + 100);
p->b[50] = 'x';
...
free(p); Test
Upd: yes it is.A conventional and working form of the same is
struct foo { int a; char b[1]; }
struct foo * p = malloc(sizeof (struct foo) + 99);
p->b[50] = 'x';
or, if the compiler allows it, struct foo { int a; char b[0]; }
struct foo * p = malloc(sizeof (struct foo) + 100);
p->b[50] = 'x';Edit: it's UB because you're accessing past the end of an array with size given at compile time.