roadmap
week 3 · day 15

C Types Are Just Sizes

char, int, long — width and sign, nothing more

Medium 30 min 180 xp
After this you can
  • 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 typeTypical size (bytes)Interpretation
char1one byte; signed or unsigned (implementation-defined)
short2signed 16-bit two's complement (unless unsigned)
int4signed 32-bit two's complement (unless unsigned)
long8 (Linux/mac)signed 64-bit
pointer (T*)8an address — always 8 bytes on x86-64
float / double4 / 8IEEE-754 floating point (a different encoding)
Key idea
Two things you already know make this click. (1) "How many bytes" is exactly the register-view idea from day 6 — a 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:

sizes.cc
1#include <stdio.h>
2int main(void) {
3 printf("char %zu\n", sizeof(char)); // 1
4 printf("short %zu\n", sizeof(short)); // 2
5 printf("int %zu\n", sizeof(int)); // 4
6 printf("long %zu\n", sizeof(long)); // 8 on Linux/mac
7 printf("ptr %zu\n", sizeof(void*)); // 8 on x86-64
8 return 0;
9}
Expected output
char  1
short 2
int   4
long  8
ptr   8

sizeof 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:

wrap.cc
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.

Predict first
unsigned int a = 10; unsigned int b = 20; unsigned int c = a - b; What is c? (int and unsigned int are 4 bytes.)
Note
Fixed-width names from <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.
finished reading?
Your task, you write the code

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.

deliverable: types.c (+ your written predictions)
build & run
$ gcc -Wall types.c -o types && ./types
self-review before running
  • 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
stretchCompile a function that takes an int vs one that takes a char and compare the disassembly — see the int use 4-byte (eax-family) operations and the char use 1-byte (al-family) operations. The C type literally selected the register width.

Self-check

01What, fundamentally, is a C type?
02How big is a pointer on x86-64?
03`unsigned int j = 0; j = j - 1;` gives:
04Why prefer uint8_t / int32_t over char / int in systems code?
0/4 correct · 0/4 checked