C pitfalls

Dangling pointer:
char* foo( void )
{
    char str[100];
    [..]
    return str;    /* WRONG, points within call stack, probable crash */
}
A number beginning with 0 (zero) is interpreted as octal, not decimal.
int limit = 0100;  /* oops, limit is assigned 64 (decimal) */
sizeof() means how many bytes (not elements) and includes trailing null byte of a string.
 
Literal strings might be compiled into a read-only segment.
One case where this pitfall manifests is by passing a literal string as a parameter to a function that tries to modify string.
Equality operators have precedence over bit operators (when in doubt, use parentheses).

IIRC, Dennis Ritchie said this was a mistake in design of C.

if ( x & MASK == VALUE )    /* WRONG: equivalent to (x & (MASK==VALUE)) */
if ( (x & MASK) == VALUE )  /* right */
char is signed, and will be promoted to a signed integer (not unsigned).
 
Bit-field structs aren't portable.
 
Body of a macro and every parameter should be parenthesized to prevent precedence bugs:
#define MAX(a,b) a > b ? a : b /* WRONG */
/* WRONG: result=1 (0xffff vs. y because '>' has more precedence than '&') */
x = 1; y = 2; result = MAX(x & 0xffff, y);
goto pitfall:
An example of a bug caused by goto. When match is false, intent is to break the inner loop, and continue outer loop.
for ( i = 0; i < MAX; ++i )
{
no_match:

   for ( j = 0; j < MAX; ++j )
   {
      if ( ! match )
         goto no_match;
   }
}
Above code is incorrect. goto skips incrementing i and causes outer loop to become stuck! A correction is to jump to bottom of outer loop:
for ( i = 0; i < MAX; ++i )
{
   for ( j = 0; j < MAX; ++j )
   {
      if ( ! match )
         goto no_match;
   }

no_match:
}

C preprocessor tricks

Macro to expand a unique name:
// Arcane indirect trick to give a variable a unique name.
// Subtlety: If written on same line, UNIQUE_NAME() expands same name.
#define UNIQUE_NAME__(NAME,LINE) NAME##LINE
#define UNIQUE_NAME_(NAME,LINE)  UNIQUE_NAME__(NAME,LINE)
#define UNIQUE_NAME(NAME)        UNIQUE_NAME_(NAME,__LINE__)

commentary/opinion

Supposedly, C has been superseded by C++, but not quite. C has advantages of simpler clearer syntax (relative to the mess that is C++23). C compilers have much higher probability of compiling correctly than C++ compilers.