5.1.2 · D2C Programming

Visual walkthrough — Data types — char, short, int, long, float, double, size_t

2,004 words9 min readBack to topic

We use only these three ideas, and we will earn each one:

  • A bit = a switch that is either 0 (off) or 1 (on).
  • A byte = 8 such switches sitting in a row.
  • Counting: how many different arrangements those switches can make.

Step 1 — A byte is 8 switches in a row

WHAT. Look at the strip below. Memory is nothing but tiny switches. We draw one byte: eight little boxes, each showing a or a . We call each box a bit (short for binary digit).

WHY. Before we can talk about ranges we must agree on the raw material. Everything — the number 65, the letter 'A', a piece of a float — lives as a pattern of these eight switches. The type is just the lens we choose later; right now there is no meaning at all, only on/off.

PICTURE. The eight boxes, numbered from bit on the right to bit on the left. Nothing means anything yet — that is the point.

Figure — Data types — char, short, int, long, float, double, size_t

Step 2 — How many patterns can 8 switches make?

WHAT. Each switch has choices. There are switches, and the choices are independent, so we multiply: Every factor of in that product is one switch. The exponent is literally the number of boxes.

WHY. This is the whole foundation of ranges. A type that owns bits can name exactly different things — not one more. If you want negative numbers, characters, or huge floats, they must all be carved out of this fixed budget of names.

PICTURE. A branching tree: bit splits into two paths, each splits again for bit , and so on. Count the leaves at the bottom — there are .

Figure — Data types — char, short, int, long, float, double, size_t

Step 3 — The unsigned lens: give each bit a place value

WHAT. Now we choose a lens. The unsigned lens says: every pattern is a plain, non-negative whole number. To read it, give bit the weight and add up the ones that are on: Here is the switch value ( or ) and is its place value, exactly like the s, s, s columns in decimal — but doubling instead of ten-ing.

WHY. We need a rule that turns switches into numbers, and this one is the simplest: it is ordinary place-value counting in base . The largest number appears when all switches are on.

PICTURE. The byte 1100 1000 with each column labelled by its weight; the on-bits ( and and ) are highlighted and summed to .

Figure — Data types — char, short, int, long, float, double, size_t

Step 4 — Wrap-around: the number line is a circle

WHAT. What happens at if you add ? All eight switches are on (); adding tries to carry past bit , that carry falls off the edge, and every switch flips back to . So . The unsigned bytes do not sit on a straight line — they sit on a clock with ticks.

WHY. This "wrap" is not a bug you can avoid; it is a direct consequence of Step 2's fixed budget. There is no room to store , so it silently becomes . This is exactly the overflow behaviour that catches beginners.

PICTURE. A circle of ticks; sits just before ; a red arrow leaps from over the top back to .

Figure — Data types — char, short, int, long, float, double, size_t

Step 5 — The signed lens: make the top bit worth a negative

WHAT. We still have patterns, but we want to name negatives too. The two's complement trick keeps every bit's weight the same — except the top bit, whose weight is made negative: Only one symbol changed: the became . That single minus sign turns the top switch into a "you owe " flag.

WHY. This is the lens C actually uses for signed char, int, etc. (see Two's Complement Representation). It is chosen because ordinary binary addition just works on it — the same adder circuit handles negatives with no special case. The pattern with only the top bit on () is now the value .

PICTURE. The same byte columns as Step 3, but the leftmost column is repainted red and relabelled . Two example patterns are read: and .

Figure — Data types — char, short, int, long, float, double, size_t

Step 6 — The signed clock and the char c = 200 mystery

WHAT. Bend the signed bytes into a clock too, but label the ticks . Now trace what happens when we write char c = 200. The pattern is still 1100 1000 (Step 3 gave us unsigned). Read through the signed lens: the top bit is on, so it contributes instead of : Equivalently, is past , so it wraps: .

WHY. This is the worked example from the parent, now seen: the bits never changed, only the lens. unsigned char u = 200 uses the Step 3 lens and stays ; char c = 200 uses the Step 5 lens and becomes . Same switches, two answers — the type is the interpretation (Type Conversion and Promotion governs which lens applies where).

PICTURE. The signed clock; a green arrow walks steps from , sailing past , over the top, and landing on .

Figure — Data types — char, short, int, long, float, double, size_t

Step 7 — Degenerate cases: and the empty budget

WHAT. Test the formulas at the extremes so no scenario surprises you.

  • bit, unsigned: . Two patterns, two values. ✔
  • bit, signed: . The single bit's weight is , so the two patterns mean and . No room for at all — the lopsidedness taken to its limit.
  • All bits zero (any ): value under both lenses (nothing is switched on, and is also ). Zero looks the same signed or unsigned.

WHY. Formulas that only work for "nice" sizes hide bugs. Checking confirms the signed formula's lopsidedness is real (one negative, zero positives) and that is the shared fixed point of both lenses.

PICTURE. Two tiny two-tick number lines side by side: unsigned and signed , with the shared circled.

Figure — Data types — char, short, int, long, float, double, size_t
Recall Why does

size_t never hit these negatives? Because sizeof measures a size, which is inherently . size_t is unsigned (Step 3 lens only), so it uses the full and can never wrap into a negative — which is exactly why size_t i; i >= 0 loops forever.


The one-picture summary

WHAT. One diagram fuses the whole derivation: the fixed budget of patterns (Step 2), split into two possible lenses — unsigned (Step 3, straight from up) and signed (Step 5, top bit flipped negative) — each closing into a clock (Steps 4 & 6), with the shared fixed point .

Figure — Data types — char, short, int, long, float, double, size_t
Recall Feynman retelling (say it to a 12-year-old)

Picture eight light-switches in a row. Each is on or off, so together they can strike different patterns — no more, because there are only eight switches. Now we decide what a pattern means. The easy way ("unsigned"): give each switch a doubling value and add up the ones that are on — this counts from up to . But we also want minus numbers. Trick: keep every switch's value the same except the leftmost, which we make worth . Now the same patterns run from up to — one extra negative because zero already took a spot on the plus side. Whichever meaning we pick, if you go past the biggest value you fall off the edge and wrap around, like a clock. So when C stores in a plain char, the switches read 11001000; through the minus- lens that's . The bits never lied — we just looked through a different lens.


Active Recall

Recall Cover and answer
  • Why patterns? ::: independent switches, each with choices, multiplied.
  • Why is unsigned max ? ::: zero eats one of the names.
  • What single change turns the unsigned lens into the signed one? ::: the top bit's weight becomes .
  • Why one more negative than positive? ::: zero sits on the positive side, using a slot.
  • Signed value of pattern 1100 1000? ::: .
  • Signed 1-bit range? ::: .
Which pattern is the most-negative signed byte?
1000 0000, worth .
Unsigned value of 1100 1000?
.
Why does char c = 200 print -56?
same bits, signed lens: .
On the byte-clock, (unsigned) equals?
(wrap-around).
Shared fixed point of both lenses?
the all-zero pattern, value .