This commit is contained in:
Miloslav Ciz 2024-12-30 20:45:53 +01:00
parent 9f0e34a0dd
commit 94fd1c5b4a
23 changed files with 1955 additions and 1939 deletions

View file

@ -6,7 +6,7 @@ This article will be focused on C specific/typical pitfalls, but of course C als
Unless specified otherwise, this article supposes the C99 standard of the C language.
**Generally**: be sure to check your programs with tools such as [valgrind](valgrind.md), [splint](splint.md), [cppcheck](cppcheck.md), UBSan or ASan, and turn on compiler auto checks (`-Wall`, `-Wextra`, `-pedantic`, ...), it's quick, simple and reveals many bugs!
**Generally**: be sure to check your programs with tools such as [valgrind](valgrind.md), [splint](splint.md), [cppcheck](cppcheck.md), UBSan or ASan, and turn on compiler auto checks (`-Wall`, `-Wextra`, `-pedantic`, ...), it's quick, simple and reveals many bugs! Also have the specification at hand, sometimes it's literally easier, safer and faster to look something up in the primary source rather than looking up opinions of the Internet people.
## Undefined/Unspecified Behavior
@ -128,7 +128,7 @@ Of course this applies to other languages as well, but C is especially known for
## Other
Basic things: `=` is not `==`, `|` is not `||`, `&` is not `&&`, array indices start at 0 (not 1) and so on. There are also some deeper gotchas like `a/*b` is not `a / *b` (the first is comment).
Basic things: `=` is not `==`, `|` is not `||`, `&` is not `&&`, `x++` is not `++x`, pointers and arrays are sometimes NOT the same thing (consider e.g. `sizeof`), array indices start at 0 (not 1) and so on. There are also some deeper gotchas like `a/*b` is not `a / *b` (the first is comment). General gotchas not specific to C still apply (messing up order of function arguments, confusing radians with degrees in trigonometric functions, error by one, ...).
Also watch out for this one: `!=` is not `=!` :) I.e. `if (x != 4)` and `if (x =! 4)` are two different things, the first means *not equal* and is usually what you want, the latter is two operations, `=` and `!`, the tricky thing is it also compiles and may work as expected in some cases but fail in others, leading to a very nasty bug. Same thing with `-=` vs `=-` and so on. See also [downto](downto.md) operator.
@ -148,11 +148,11 @@ Also `putchar('a')` versus `putchar("a")` ;) Only the first one is correct of co
Another possible gotcha: `const char *myStrings[] = {"abc", "def", "ghi"};` vs `const char *myStrings[] = {"abc", "def" "ghi"};`. In the latter we forgot a comma, but it's still a valid code, in the array there are now only two strings, the latter being "defghi". Writing the expected array size would help spot this as it wouldn't match.
**Stdlib API can be [trollish](trolling.md)**, for example the file printing functions: *fprintf* expects the file pointer as first argument while *fputs* expects it as last, so to print hello you can do either `fprintf(file,"hello")` or `fputs("hello",file)` -- naturally this leads to fucking up the order sometimes and doing so even compiles (both arguments are pointers), the running code then crashes.
**Stdlib API can be [trollish](trolling.md)**, for example the file printing functions: *fprintf* expects the file pointer as first argument while *fputs* expects it as last, so to print hello you can do either `fprintf(file,"hello")` or `fputs("hello",file)` -- naturally this leads to fucking up the order sometimes and doing so even usually compiles (both arguments are pointers, although not of the same type so you'll be warned), the running code then crashes.
Watch out for **operator precedence**! C infamously has weird precedence with some special operators, bracket expressions if unsure, or just to increase readability for others. Also nested ifs with elses can get tricky -- again, use curly brackets for clarity in your spaghetti code.
**[Preprocessor](preprocessor.md) can give you headaches** if you use it in overcomplicated ways -- ifdefs and macros are fine, but too many nesting can create real mess that's super hard to debug. It can also quite greatly slow down compilation. Try to keep the preprocessing code simple and flat.
**[Preprocessor](preprocessor.md) can give you headaches** if you use it in overcomplicated ways -- ifdefs and macros are fine, but too many nesting can create real mess that's super hard to debug. It can also quite greatly slow down compilation. Try to keep the preprocessing code simple, orthogonal and flat.
Watch out for **[macro](macro.md) arguments**, always bracket them because they get substituted on text level. Consider e.g. a macro `#define divide(a,b) a / b`, and then doing `divide(3 + 1,2)` -- this gets expanded to `3 + 1 / 2` while you probably wanted `(3 + 1) / 2`, i.e. the macro should have been defined as `#define divide(a,b) (a) / (b)`.