02. Data Types · Radix · stdint · sizeof
An MCU's registers have exact widths (8/16/32-bit) and RAM is only a few KB. "Exactly how many bits is this variable?" decides whether it fits a register. We look at the reality of types (their width): why stdint.h's fixed-width types are the de-facto embedded standard, radix notation, sizeof, and the wrap-around when an unsigned value exceeds its max — all verified with PC gcc.
What you'll learn
- 1Understand that basic type sizes vary by chip (8051 int=16-bit)
- 2Use stdint.h's uint8_t/uint16_t/uint32_t tied to register width
- 3Print a value in decimal·hex·octal·binary with printf formats
- 4Confirm a type's byte size with sizeof
- 5Understand unsigned wrap-around and signedness reinterpretation
Introduction
On a PC you rarely care how many bytes an int is. In embedded, "exactly how many bits" decides register fit and memory waste. This lesson examines the reality of types (their width) with pure C logic, no board needed.
Key concepts
1) Type sizes vary by chip
| Type | 8051(C51) | ARM(MDK) | PC(x86-64) |
|---|---|---|---|
| char | 1 | 1 | 1 |
| int | 2 (16-bit) | 4 (32-bit) | 4 (32-bit) |
| long | 4 | 4 | 8 |
On 8051, `int count;` counting to 50000 overflows the 16-bit limit and gives garbage. The PC habit "int is enough" breaks here.
2) stdint.h — width baked into the name
| Type | Width | Range (unsigned) |
|---|---|---|
| uint8_t | 8-bit | 0–255 |
| uint16_t | 16-bit | 0–65535 |
| uint32_t | 32-bit | 0–~4.29B |
- 8-bit port/register (8051 P1) → uint8_t
- 16-bit timer counter → uint16_t
- 32-bit STM32 register → uint32_t
3) Radix and printf formats
Hex is natural for registers·bits (one hex digit = 4 bits). Notation: decimal `42`, hex `0x2A`, octal `052` (beware a leading 0!). Formats: `%u` (dec), `%x`/`%X` (hex), `%o` (oct). Standard printf has no binary format, so extract bits with `(v>>i)&1`.
4) Wrap-around and signedness
uint8_t c = 255;
c = c + 1; /* 0, not 256 (wrap-around) */
/* The same 0xFF bits read differently by signedness */
/* (uint8_t)0xFF = 255, (int8_t)0xFF = -1 (two's complement) */Core example
Print fixed-width sizes with sizeof and show one value (0x2A) side by side in several radixes.
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint8_t reg = 0x2A; /* hex 0x2A = decimal 42 */
printf("sizeof(uint8_t) = %u\n", (unsigned)sizeof(uint8_t));
printf("sizeof(uint32_t) = %u\n", (unsigned)sizeof(uint32_t));
printf("dec=%u hex=0x%02X oct=0%o\n", (unsigned)reg, reg, reg);
return 0;
}
// dec=42 hex=0x2A oct=052gcc main.c -o main && ./mainCommon mistakes
Q. I counted to 50000 with int and got weird values.
A. On 8051, int is 16-bit (signed: -32768–32767). Use uint32_t (or unsigned long) for large numbers.
Q. for (uint8_t i = 10; i >= 0; i--) never ends.
A. An unsigned i is never negative. At 0, i-- wraps to 255 (not -1), so i >= 0 stays true forever. Use a signed type for countdowns, or redesign the condition.
Q. I stored 0xFF read from a register and got -1.
A. If the receiving variable is int8_t/signed char, 0xFF is -1 (two's complement). Receive "bit bundles" in an unsigned type (uint8_t) to read 255.
Summary
- Basic type sizes vary by chip (int: 16-bit on 8051, 32-bit on ARM/PC)
- stdint.h's uint8_t/uint16_t/uint32_t are 1/2/4 bytes everywhere — the embedded standard
- Hex (0x) is natural for registers·bits; beware a leading 0 (octal)
- sizeof confirms the real byte count
- Unsigned values wrap to 0 past their max
Exercises
- Increment a uint8_t from 250 by 1 ten times and watch 255 wrap to 0
- Write a program that prints 0xA5 in decimal·hex·octal·binary
- Print sizeof(int)/sizeof(long) on PC gcc, then compare with the 8051 row of the table
All lecture materials and example code (with homework and answers) are openly available on GitHub.
View on GitHub ↗