C Types Are Just Sizes
char, int, long — width and sign, nothing more
- › Explain what a C type really is: a byte-width plus an interpretation rule
- › State the sizes of char, short, int, long, and a pointer, and check them yourself
- › Predict signed vs unsigned behaviour and overflow from the width and sign
- › Connect a variable's type to the register width and memory it compiles to
The demystification
Now we cross into C — but you're not starting from zero, you're starting from the machine. Here is the whole secret of a C type, and it's smaller than people pretend: a type is two facts — how many bytes the value occupies, and how those bytes are interpreted (as an unsigned number, a two's -complement signed number, or a float). That's it. Everything else about types follows from those two facts and the ideas you already own from week 1.
| C type | Typical size (bytes) | Interpretation |
|---|---|---|
| char | 1 | one byte; signed or unsigned (implementation-defined) |
| short | 2 | signed 16-bit two's complement (unless unsigned) |
| int | 4 | signed 32-bit two's complement (unless unsigned) |
| long | 8 (Linux/mac) | signed 64-bit |
| pointer (T*) | 8 | an address — always 8 bytes on x86-64 |
| float / double | 4 / 8 | IEEE-754 floating point (a different encoding) |
char uses al (1 byte), an int uses eax (4), a long or pointer uses
rax (8). The C type *chooses the width*. (2) "How it's interpreted" is day 1–2: the same bytes
are unsigned or signed depending on the type, and signed overflow wraps by two's complement. C types
are just names for "this many switches, read this way."Don't trust sizes — measure them
The sizes above are *typical*, not guaranteed by the language (C only guarantees minimums and an
ordering). A professional never assumes; they ask the compiler with sizeof, which yields the exact
byte count on *this* platform:
1#include <stdio.h>2int main(void) {3 printf("char %zu\n", sizeof(char)); // 14 printf("short %zu\n", sizeof(short)); // 25 printf("int %zu\n", sizeof(int)); // 46 printf("long %zu\n", sizeof(long)); // 8 on Linux/mac7 printf("ptr %zu\n", sizeof(void*)); // 8 on x86-648 return 0;9}char 1
short 2
int 4
long 8
ptr 8sizeof isn't a function — it's a compile-time question answered by the type system, and the answer
is the number of bytes the value occupies in memory. When you later say "this variable is 4 bytes,"
you mean it lives in 4 of the memory cells from day 4.
Sign and width decide behaviour
Because a type is width + interpretation, the *type alone* predicts the tricky behaviour:
1unsigned char u = 255;2u = u + 1; // wraps to 0 (8 bits, unsigned: 255+1 mod 256)3 4signed char s = 127;5s = s + 1; // wraps to -128 (8 bits, two's complement overflow)6 7int i = 3;8unsigned int j = 0;9j = j - 1; // 4294967295, not -1 (unsigned can't be negative)Every one of those is a day 1–2 fact wearing a C type. The unsigned char wraps at 256 because it's
8 bits read as unsigned; the signed char wraps to −128 by two's complement; 0u - 1 is a huge
number because unsigned has no negatives. If you know the width and the sign, you can *predict* the
result — no surprises, no memorization.
unsigned int a = 10; unsigned int b = 20; unsigned int c = a - b; What is c? (int and unsigned
int are 4 bytes.)<stdint.h> — uint8_t, int32_t, uint64_t — say the width *and* sign
explicitly, so you never depend on "typical" sizes. In systems and security code you'll prefer these:
uint8_t is unambiguously one unsigned byte, everywhere. When the exact number of bytes matters
(and at this level it always does), spell it out.Measure and predict
In types.c: (1) print sizeof for char, short, int, long, a pointer, and a couple of <stdint.h> types, and note the exact bytes on your machine. (2) BEFORE running, predict on paper the result of: unsigned char 255+1, signed char 127+1, and (unsigned)(0 - 1). Then print them and confirm each against your prediction, explaining the result from the type's width and sign. Everything you need is this lesson plus days 1-2.
$ gcc -Wall types.c -o types && ./types- › You reported the exact sizeof for each type on your machine
- › You predicted all three wrap results before running
- › You can explain each wrap from the type's width and interpretation
- › You can state why a `for (unsigned i = n; i >= 0; i--)` loop never ends