5.1.3 · D5C Programming

Question bank — Type sizes — sizeof, platform dependency, stdint.h (int32_t etc.)

1,338 words6 min readBack to topic

This bank is deliberately non-computational — there is no arithmetic to grind. Every item is about whether a belief is true and why. The word "justify" is not optional: a bare "true/false" earns nothing, because the whole skill is the reasoning that survives when you switch machines.


True or false — justify

C guarantees int is exactly 4 bytes on every hosted platform.
False. C only guarantees int is at least 16 bits; 4 bytes is a mainstream 32/64-bit coincidence, and 16-bit targets use 2-byte int.
sizeof(char) can legally be 2 on some machine.
False. sizeof counts in units of char, and a char is "1 char" by definition — so sizeof(char) is 1 on every conforming implementation, always.
A char is guaranteed to be exactly 8 bits.
False. The standard promises char is at least 8 bits (CHAR_BIT ≥ 8); rare DSPs use larger char. It's sizeof(char)==1 that's guaranteed, not "8 bits".
On 64-bit Windows, sizeof(long) == sizeof(long long).
False. Windows is LLP64: long is 4 bytes and long long is 8, so they differ. They'd only match on LP64 (Linux/macOS 64-bit).
If two types have the same sizeof, they represent the same set of values.
False. int and unsigned int share a size but hold different ranges (one signed, one unsigned), and int vs float share 4 bytes yet encode numbers completely differently. See Two's complement & integer overflow.
sizeof(void*) == sizeof(int) on all platforms.
False. On LP64/LLP64 a pointer is 8 bytes while int is 4. Storing a pointer in an int truncates it — use intptr_t if you must.
int32_t is guaranteed to exist on every conforming compiler.
False. int32_t is only defined if the platform has an exact 32-bit two's-complement type. int_least32_t and int_fast32_t are the ones that are always available.
The size ordering char ≤ short ≤ int ≤ long ≤ long long is guaranteed.
True. The standard mandates that ordering, even though the exact bytes vary. So int can never be strictly wider than long.
sizeof evaluates its operand at run time.
False. sizeof is a compile-time operator; the size is fixed when compiling. (The one exception is variable-length arrays, which most style guides avoid.)

Spot the error

printf("%d\n", sizeof(int));

::: Wrong specifier. sizeof yields size_t (unsigned, often 64-bit); printing it with %d is undefined behavior. Use %zu. See printf format specifiers.

int count = sizeof(arr) / sizeof(arr[0]); // arr is a function parameter

::: Inside a function, an array parameter decays to a pointer (Pointers and array decay), so sizeof(arr) is the pointer size, not the array size — the count is wrong. Pass the length as a separate argument.

unsigned char *img = malloc(1024);
long total = sizeof(img); // "bytes I allocated"

::: sizeof(img) is the size of the pointer (8 bytes on 64-bit), not the 1024 bytes it points at. sizeof never knows how big a malloc'd block is; you must track that yourself.

struct Packet { uint16_t port; uint32_t ip; };
// assume sizeof(Packet) == 6

::: Wrong: alignment forces padding. The compiler likely inserts 2 bytes so ip sits on a 4-byte boundary, giving sizeof == 8. Fixed-width members do not remove padding — see Struct padding & alignment.

char c = getchar();
if (c == EOF) { /* done */ }

::: If plain char is unsigned on this platform, it can never equal EOF (a negative int), so the loop never ends. getchar returns int for exactly this reason — store it in an int.

char b = 200; // hold a byte value

::: On a signed-char platform, 200 overflows the signed range and stores a negative value. For a raw 0–255 byte, use unsigned char or uint8_t.


Why questions

Why does C leave int's size unspecified instead of just fixing it at 4?
So the compiler can pick the CPU's fastest natural word size on each machine — speed over uniformity. Fixing it would slow C down on 16-bit and unusual hardware.
Why is sizeof(char) defined to be 1 rather than "8 bits"?
Because sizeof is a ratio of storage to char-storage; making char the unit means all other sizes are counted in chars, so char itself is trivially 1.
Why must you send network/file-format structs with int32_t instead of int?
A wire or disk layout has a fixed byte count. Plain int/long differ across ABIs, so two machines would disagree on the layout; int32_t pins it. See Endianness & serializing data.
Why does sizeof a / sizeof a[0] give the element count portably?
It's total bytes ÷ bytes-per-element, so the char-size cancels and the answer never depends on how wide int happens to be.
Why does sizeof return size_t rather than int?
An object can be larger than INT_MAX bytes; size_t is an unsigned type guaranteed big enough to hold any object's size. More on size_t and ptrdiff_t.
Why is long the classic portability landmine and not int?
Because int is 4 bytes on essentially every 32/64-bit desktop, but long splits: 8 on Linux/macOS, 4 on Windows. Same source, different width, silent bug.
Why prefer uint8_t over plain char for raw bytes?
Plain char's signedness is implementation-defined, so bit-twiddling and comparisons behave differently per platform; uint8_t is unambiguously 0–255.

Edge cases

What is sizeof(sizeof(int))?
It's sizeof(size_t) — because sizeof(int) has type size_t. So it's the size of size_t itself (typically 8 on 64-bit), not related to int.
Does sizeof on a bit-field like x:3 give a fraction?
No — sizeof never sees a bit-field; you can't even apply it to one. sizeof only measures whole objects/types in whole char units.
On a 16-bit machine where int is 2 bytes, is int32_t still 4 bytes?
Yes, if it exists. Fixed-width types are defined by their name's number, independent of the native int. The compiler builds int32_t from whatever type is exactly 32 bits (here, long).
Can sizeof(struct S) be smaller than the sum of its members' sizes?
No — padding can only add bytes for alignment, never remove them. The struct is at least the sum of members, usually more.
If CHAR_BIT were 16, would int32_t still be 4 chars wide?
No. int32_t is exactly 32 bits; with 16-bit chars that's 32/16 = 2 chars, so sizeof(int32_t) would be 2. The name promises bits, and sizeof counts chars.
Is sizeof(x) where x is undeclared a compile error?
Yes — sizeof needs a known type, so an undeclared name fails to compile. But sizeof never reads a valid x, so sizeof(*p) is fine even when p is a null pointer.

Recall

Recall The single sentence that prevents most portability bugs

"Plain int/long promise a minimum, never an exact width — if I need exact bytes I write the number: int32_t."