1 << 31 looks like the obvious way to set the top bit of a 32-bit integer — and it’s signed integer overflow, which C defines as undefined behavior. The scariest part, and the reason this question from our C Programming Quiz App earns its “Hard” rating: unlike most UB in this series, the compiler emits no warning at all. It compiles silently, prints a plausible number, and waits for an optimizer or platform change to betray you. We ran it; here’s the evidence.
The Quiz Question
int a = 1;
printf("%d", a << 31);
What is the output of this code?
- 2147483648
- -2147483648
- 0
- Undefined behavior
The Correct Answer: Undefined Behavior
a is a signed int — on every mainstream platform today, 32 bits with a sign bit at position 31. The shift rule (C11 §6.5.7p4) says: for signed E1 << E2, if E1 × 2^E2 is not representable in the result type, the behavior is undefined. 1 × 2³¹ = 2,147,483,648, but a 32-bit int maxes out at 2,147,483,647. One too far — undefined.
Watch how quietly it goes wrong. gcc 13.3, all warnings on:
$ gcc -Wall -Wextra quiz.c -o quiz (no warnings — silence) $ ./quiz -2147483648
No diagnostic, and an output that looks intentional. Only UndefinedBehaviorSanitizer tells the truth:
$ gcc -fsanitize=undefined quiz.c -o quiz $ ./quiz quiz.c:5:22: runtime error: left shift of 1 by 31 places cannot be represented in type 'int' -2147483648
Why Each Wrong Answer Is Wrong
Why not 2147483648?
That’s the mathematically correct value of 2³¹ — and it does not fit in a 32-bit signed int, whose range is −2,147,483,648 to +2,147,483,647. The result type of a << 31 is still int, so there’s nowhere to put this value. (Printing it would also need %u or a wider type — %d can’t display it.)
Why not −2147483648?
The trap option, because it’s what we actually observed. On a two’s-complement machine, the bit pattern 0x80000000 reinterpreted as signed is −2,147,483,648 — so that’s what falls out when the compiler takes the naive path. But nothing obligates it to. Once the optimizer can see the overflow, it’s allowed to assume it never happens and transform your code on that assumption — the same class of UB-based optimization that famously deletes overflow checks like if (x + 1 < x). Observed ≠ guaranteed.
Why not 0?
Zero would require the 1-bit to be shifted out entirely — that’s what 1 << 32 might naively suggest (also UB, for a different reason: shift count equal to the type width). At 31 places the bit lands exactly on the sign bit, not past it.
The Right Way to Set Bit 31
Do bit manipulation in unsigned types, where shifts and wraparound are fully defined:
unsigned int mask = 1u << 31; /* well-defined: 2147483648 */
printf("%u", mask); /* %u for unsigned */
That one-character change — 1u instead of 1 — moves the whole operation into unsigned arithmetic. This is why you’ll see 1u << n, 0x1Fu, and UINT32_C(1) all over production embedded code and kernel headers: register masks, flag words, and hash functions live in unsigned types precisely to make overflow behavior defined.
A note on the standards timeline: C23 finally mandates two’s-complement representation for signed integers, and tightens up some shift semantics. But gcc 13’s C23 mode (-std=c2x) still flags 1 << 31 at runtime under UBSan — and every compiler default in use today targets C17 or earlier, where it’s unambiguously UB. Portable code should treat signed overflow as undefined, full stop.
How to Catch This Bug
gcc -fsanitize=undefined file.c # UBSan: "left shift of 1 by 31 places..." clang -fsanitize=undefined file.c # same check gcc -ftrapv file.c # trap on signed overflow (add/sub/mul)
This is the one bug in our undefined-behavior series that -Wall -Wextra does not catch — the shift amount and operand are constant-foldable, yet gcc stays silent. Make -fsanitize=undefined part of your test builds; it’s the only reliable net for this class.
Frequently Asked Questions
Is 1 << 31 always undefined in C?
For a 32-bit int, yes, through C17 — the result 2³¹ isn’t representable in the signed result type. Write 1u << 31 to make it well-defined unsigned arithmetic.
Why doesn’t gcc warn about it?
Shift-overflow detection isn’t part of -Wall -Wextra for this pattern (a constant shift applied to a variable). gcc does warn on fully constant expressions in some contexts, but the general case is only caught at runtime by -fsanitize=undefined.
Is unsigned overflow also undefined?
No — unsigned arithmetic wraps modulo 2ᴺ by definition. That’s exactly why bit manipulation, checksums, and hash functions are written with unsigned types.
Related Reading
- Bit Manipulation in C – Set, Clear, Toggle, and Count Bits
- Arithmetic Operators in C
- C Aptitude Questions and Answers
- volatile Keyword in C
Recommended Books
- The C Programming Language – Kernighan & Ritchie (India) | Amazon.com
- C Programming: A Modern Approach – K.N. King (India) | Amazon.com
This question is #154 in the C Programming Quiz App — 155 questions with explanations covering operators, memory, pointers, and more.
Download on Google Play →