4 ms·
C Traps and Pitfalls (1988) [pdf]
- aragonite 2y agoWikipedia article about the book of the same name: https://en.wikipedia.org/wiki/C_Traps_and_Pitfalls https://en.wikipedia.org/wiki/C_Traps_and_Pitfalls
- pjmlp 2y agoFor all practical purposes, as true in 2024, as in 1988, except for the few ones that actually care about using modern analysis tooling in their development workflows.
- rk06 2y ago“Expect C programming”[1] is a fantastic take on c language. And its idiosyncracies [1] https://www.amazon.in/Expert-Programming-Peter-van-Linden/dp/0131774298 https://www.amazon.in/Expert-Programming-Peter-van-Linden/dp...
- llmblockchain 2y agoThis is one of my favorite programming books of all time. A lot of small light bulbs went off reading that book. It really opened my mind to C and its oddities.
- vrighter 2y agothe part about right shift not being equivalent to a division by 2 even if sign bits are copied didn't really convince me. -1/2 = -0.5 floor of that is -1. -1 >> 1 == -1 What's the issue? It's integers. The floor is implied.
- wahern 2y agoInteger division rounds to 0, not negative infinity. -1 / 2 == 0, or -0 if you're lucky enough to have the hardware. For ones' complement and signed magnitude, though, right shift of a negative number also rounds to 0, I think.
- adrian_b 2y agoAs another poster has already said, the floor is not implied. Right shift is equivalent with division if integer division were defined in a way consistent with two's complement number encoding. Alas, that is not true. The integer division instructions of most CPUs have definitions that are inherited from extremely ancient computers that used sign-magnitude number encoding and they truncate the quotient towards zero. There are many possible definitions of integer division and this is one of the worst, by failing to have many of the desirable mathematical properties (for instance it partitions the integer numbers into equivalence classes that have different sizes, which is bad for something like a histogram). The C language just inherits the bad integer division definition implemented in hardware, instead of using a better definition that would result in lower performance due to additional overhead. Therefore it ends with two different kinds of integer divisions, one for "/" and another for ">>", which coincide for non-negative numbers, but which differ for negative numbers. In my opinion this is a mistake that exists in almost all programming languages. They should have used a better definition for the integer division and remainder operations than the one implemented in hardware. The use of integer division with negative numbers happens only seldom, so its performance is unlikely to be important, but giving more useful and consistent results can simplify some programs and avoid surprising bugs. Now it has become a chicken and egg problem. The CPU hardware continues to implement the bad definition, because that is what the popular programming languages expect, while the programming languages continue to use the bad definition because it is implemented in hardware. See "The Euclidean definition of the functions div and mod": https://dl.acm.org/doi/10.1145/128861.128862 https://dl.acm.org/doi/10.1145/128861.128862
- MaxBarraclough 2y agoI wasn't aware of this gem of C minutiae: Compound assignment operators such as += are really multiple tokens. Thus, a + /* strange */ = 1 means the same as a += 1
- omoikane 2y agoIt would be a syntax error in C++. <source>:3:11: error: expected primary-expression before '=' token 3 | a + = 1; | ^
- MaxBarraclough 2y agoA compiler error isn't definitive about the language standard, but as a quick google search didn't turn up anything definitive, I'll do the same: I now doubt whether it's true for either language. gcc 11 does not treat i + = 8; as equivalent to i += 8;, I get the kind of error message I'd have expected to see prior to reading the article. This occurs even if passing -std=c89 (as C89 is the oldest version of the C language that it seems to support). Perhaps it was true prior to the C89 standard?
- omoikane 2y agoI think it's describing a particular lexer or preprocessor bug that GCC never had, so "a + = 1" should have been a syntax error in C89 as well. Someone mirrored GCC 0.9 (1987), the relevant bits start here: https://github.com/huangguiyang/gcc-0.9/blob/a83ea1b45dc3187d9fc4f7a3d8091c1454991afe/parse.y#L1296 https://github.com/huangguiyang/gcc-0.9/blob/a83ea1b45dc3187... // Read the next non-whitespace character. 1296: c = skip_white_space(); // Match it against '+' 1618: case '+': // Tentatively give it the token 'PLUS_EXPR' 1637: case '+': 1638: yylval.code = PLUS_EXPR; break; // Read the next character, but note that whitespaces are NOT skipped here. 1663: c1 = getc(finput); // Match the second character against '=' 1665: if (c1 == '=') ... 1678: value = ASSIGN; goto done; // Unread the next charcter if it didn't match. 1700: ungetc (c1, finput); "a +/* */= 1" wouldn't have parsed either, because /**/ are translated to spaces by the preprocessor. https://github.com/huangguiyang/gcc-0.9/blob/a83ea1b45dc3187d9fc4f7a3d8091c1454991afe/cccp.c#L974 https://github.com/huangguiyang/gcc-0.9/blob/a83ea1b45dc3187...*