``` {=html} ``` ##### [digital-domain.net](https://digital-domain.net/) # Some common C mistakes First of all I'm talking specifically about C, _not_ C++. However some of this _may_ also apply to C++. ## C only supports pass-by-value C really only supports pass by value. Pass by reference can be accomplished by using pointers. A fairly common thing I see in codebases is something like the following ```C #include #include struct s { int i; }; static void f(struct s *s) { /* Do something with s */ free(s); s = NULL; } int main(void) { struct s *s = malloc(sizeof(*s)); printf("s: %p\n", s); f(s); printf("s: %p\n", s); return 0; } ``` Which produces the following output ``` $ ./pass-by-value s: 0x8092a0 s: 0x8092a0 ``` In the function _f()_ we pass a pointer to a struct. Something is done with _s_, then it's free(3)'d. So far, so good, but then a common thing to do is to _NULL_ the free'd pointer. However that will have no effect outside _f()_. The pointer address was passed to _f()_ on the stack and now has no direct connection to _s_ in _main()_. If you want to really _NULL_ out the pointer, then you need to pass it in by _reference_ by passing in a pointer to it. E.g ```C static void f(struct s **s) { /* Do something with *s */ free(*s); *s = NULL; } ``` then in _main()_ we pass in _s_ like ```C f(&s); ``` which now produces ```console $ ./pass-by-value s: 0x8092a0 s: (nil) ``` ## Function prototypes with no arguments It is quite common to see things like ```C void f() { /* Do something */ } ``` where _f()_ takes no arguments. Writing it like the above has different meanings in C and C++. In C++ it means a function that takes no arguments. In C it means the number (and type) of arguments hasn't been specified (note, this is different to using '...' to specify a variable number of arguments). The _correct_ way to do this in C is ```C void f(void) { /* Do something */ } ``` You can enable -Wstrict-prototypes in both _gcc_ and _clang_ to catch these. E.g ```console $ gcc -Wstrict-prototypes -c empty-func-args.c empty-func-args.c:1:6: warning: function declaration isn’t a prototype [-Wstrict-prototypes] 1 | void f() | ^ ``` ```console $ clang -Wstrict-prototypes -c empty-func-args.c empty-func-args.c:1:7: warning: this old-style function definition is not preceded by a prototype [-Wstrict-prototypes] void f() ^ 1 warning generated. ``` ## Casting the return type of malloc et al OK, so this one might be slightly controversial... Another common thing I see is code like ```C int *vals = (int *)malloc(sizeof(int) * nr_vals); ``` Note the casting of the return from malloc(3). While this may be required in C++, in C - It's superfluous. Why clutter the code with unnecessary casts? - It can actually hide bugs. OK, how you might ask... Well lets say you forgot to ```C #include ``` so you don't have the declaration of malloc(3), now the compiler may assume that the return type of malloc(3) is an int. Now you may be getting a truncated memory address returned. NOTE: With current compilers this is not such a problem anymore and you will get a clear warning about the missing declaration. But the first point still stands. ## strsep(3) and invalid free(3) Lets take the following example ```C #include #include #include int main(void) { char *str = strdup("Hello World"), *tkn; printf("str [%s@%p]\n", str, str); tkn = strsep(&str, " "); printf("tkn [%s] str [%s@%p]\n", tkn, str, str); free(str); return 0; } ``` When run we get the following ```console $ ./strsep-invalid-free str [Hello World@0xf77e2a0] tkn [Hello] str [World@0xf77e2a6] free(): invalid pointer Aborted (core dumped) ``` This is due to *str* being modified, as you can see its address changes. Thus when it’s passed to free(3) *str* is no longer pointing to the address that was originally allocated. Rather you need to take a *copy* of the *str* pointer and pass that to free(3), e.g. ```C char *p; ... p = str; /* Do strsep(3)'s */ free(p); ``` Similarily if you set *str* to *NULL* in strsep(3) or strtok(3) watch you don't end up just doing *free(NULL)*.