Update
This commit is contained in:
parent
95e6641b63
commit
6babe76b2a
24 changed files with 1893 additions and 1873 deletions
|
@ -30,7 +30,7 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
**Order of evaluation of operands and function arguments is not specified**. I.e. in an expression or function call it is not defined which operands or arguments will be evaluated first, the order may be completely random and the order may differ even when evaluating the same expression at another time. This is demonstrated by the following code:
|
||||
**Order of evaluation of operands and function arguments is generally not specified**. I.e. in an expression or function call it is not defined which operands or arguments will be evaluated first, the order may be completely random and the order may differ even when evaluating the same expression at another time. Some operators, like `&&` and `||`, may be exception to this, but these are few. This is demonstrated by the following code:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
@ -50,6 +50,8 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
Watch out especially for cases e.g. with [pseudorandom](pseudorandomness.md) generators, i.e. things like: `if ((randomNum() % 2) & (randomNum() < x)) ...` -- this may introduce undesired [nondeterminism](determinism.md), i.e. the code may give different results on different computers, compilers or just between different runs. Using `&&` here would probably help as that is a short circuit operator that has defined order of evaluation from left to right. You may always enforce specific order of evaluation by just sequentially computing it in several steps, like for example: `int cond = randomNum() % 2; cond &= randomNum() < x; if (cond) ...`.
|
||||
|
||||
**Overflow behavior of signed type operations is not specified.** Sometimes we suppose that e.g. addition of two signed integers that are past the data type's limit will produce two's complement overflow (wrap around), but in fact this operation's behavior is undefined, C99 doesn't say what representation should be used for numbers. For [portability](portability.md), predictability and preventing bugs **it is safer to use unsigned types** (but safety may come at the cost of performance, i.e. you prevent compiler from performing some optimizations based on undefined behavior).
|
||||
|
||||
**Bit shifts by type width or more are undefined.** Also bit shifts by negative values are undefined. So e.g. `x >> 8` is undefined if width of the data type of `x` is 8 bits or fewer.
|
||||
|
@ -144,6 +146,8 @@ Beginners similarly often forget breaks in switch statement, which works but usu
|
|||
|
||||
Also `putchar('a')` versus `putchar("a")` ;) Only the first one is correct of course.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue