shifting too far and modifying a string literal
Two small, everyday-looking operations turn out to be undefined behavior, and both surprise people because they 'feel' harmless. The first is shifting a number's bits by too many places. The second is changing the letters of a string you wrote directly in quotes. Each looks innocent and may even appear to work, which is exactly why they are worth flagging together.
Take the shift first. The expression x << n moves the bits of x left by n positions, and x >> n moves them right. C requires that n be at least 0 and STRICTLY LESS than the number of bits in the (promoted) type of x. For a 32-bit value, n must be 0..31; writing x << 32 or x << 40, or any negative shift count, is undefined behavior — it is not defined to give 0, and on many CPUs the hardware actually uses only the low bits of n, so x << 32 may behave like x << 0 and silently give back x. Shifting a negative signed value left is also undefined. Now the string literal. When you write char *s = "hello"; the text "hello" lives in read-only memory shared across the whole program, and s points INTO it. Writing s[0] = 'H'; tries to modify that read-only data and is undefined behavior — typically a crash, but not guaranteed. The fix is to copy the literal into a writable array: char s[] = "hello"; gives you your own modifiable copy.
Why this matters: both mistakes hide behind code that compiles without complaint and sometimes runs without visible harm, then breaks on a different CPU, a different compiler, or a higher optimization level. The shift trap commonly appears in bit-manipulation and serialization code where a shift amount comes from a variable that can reach the type's width. The string-literal trap appears whenever a 'char *' from a literal is treated as if it were an editable buffer. Declaring literal pointers as 'const char *' lets the compiler catch the write at compile time, and validating shift counts against the type width keeps shifts in range.
uint32_t x = 1; uint32_t y = x << 32; /* UB: count must be 0..31, not 32 */ char *s = "hello"; /* points into read-only memory */ s[0] = 'H'; /* UB: modifying a string literal */ char buf[] = "hello"; /* OK: a writable copy on the stack */ buf[0] = 'H'; /* fine */
A shift count equal to the type width is UB; writing through a literal pointer is UB. Keep shift counts in range and copy literals into a char array to edit them.
x << 32 on a 32-bit type is NOT defined to be 0; many CPUs mask the count and give x << 0 instead. And a literal pointer should be 'const char *' so the compiler flags any attempt to write through it.