02. 자료형·진수·stdint·sizeof
MCU의 레지스터는 폭이 정확히 정해져 있고(8/16/32비트) RAM은 수 KB뿐입니다. "이 변수가 정확히 몇 비트인가"가 곧 레지스터에 제대로 들어가는지를 결정합니다. stdint.h 의 폭 고정 타입이 왜 임베디드의 사실상 표준인지, 10진·16진·2진 표기, sizeof, 그리고 부호 없는 값이 최댓값을 넘을 때의 랩어라운드까지 PC의 gcc 로 직접 확인합니다.
이 강의에서 배우는 것
- 1C 기본 자료형 크기가 칩마다 다름을 이해한다(8051 int=16비트)
- 2stdint.h 의 uint8_t/uint16_t/uint32_t 를 레지스터 폭과 연결해 쓴다
- 3같은 수를 10진·16진·8진·2진으로 표기하고 printf 포맷으로 출력한다
- 4sizeof 로 자료형의 바이트 크기를 확인한다
- 5부호 없는 자료형의 랩어라운드와 부호 해석 차이를 이해한다
소개
PC에서는 int 가 몇 바이트인지 거의 신경 쓰지 않습니다. 하지만 임베디드에서는 "이 변수가 정확히 몇 비트인가"가 레지스터 적합성과 메모리 낭비를 좌우합니다. 이번 편은 보드 없이 순수 C 로직으로 자료형의 실체(폭)를 들여다봅니다.
핵심 개념
1) 자료형 크기는 칩마다 다르다
C 표준은 최소 크기만 보장하고 정확한 바이트 수는 컴파일러·칩이 정합니다. int 하나도 환경에 따라 폭이 다릅니다.
| 자료형 | 8051(C51) | ARM(MDK) | PC(x86-64) |
|---|---|---|---|
| char | 1 | 1 | 1 |
| int | 2(16비트) | 4(32비트) | 4(32비트) |
| long | 4 | 4 | 8 |
8051에서 `int count;` 로 50000까지 세려 하면 16비트 한계에 걸려 엉뚱한 값이 나옵니다. "int면 충분하겠지"라는 PC 습관이 깨지는 대표 지점입니다.
2) stdint.h — 폭이 이름에 박힌 타입
| 타입 | 폭 | 범위(unsigned) |
|---|---|---|
| uint8_t | 8비트 | 0~255 |
| uint16_t | 16비트 | 0~65535 |
| uint32_t | 32비트 | 0~약 42억 |
- 8비트 포트/레지스터(8051 P1) → uint8_t
- 16비트 타이머 카운터 → uint16_t
- 32비트 STM32 레지스터 → uint32_t
3) 진수와 printf 포맷
레지스터·비트는 16진수가 자연스럽습니다(16진 한 자리 = 4비트). 표기: 10진 `42`, 16진 `0x2A`, 8진 `052`(선행 0 주의!). 포맷: `%u`(10진), `%x`/`%X`(16진), `%o`(8진). 표준 printf 에는 2진 포맷이 없어 `(v>>i)&1` 로 직접 뽑습니다.
4) 랩어라운드와 부호 해석
uint8_t c = 255;
c = c + 1; /* 256 이 아니라 0 (wrap-around) */
/* 같은 0xFF 비트도 부호 해석에 따라 다르다 */
/* (uint8_t)0xFF = 255, (int8_t)0xFF = -1 (2의 보수) */핵심 예제
폭 고정 타입의 크기를 sizeof 로 찍고, 한 값(0x2A)을 여러 진수로 나란히 출력합니다.
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint8_t reg = 0x2A; /* 16진 0x2A = 10진 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 && ./main자주 하는 실수
Q. int 로 5만까지 셌는데 값이 이상해요.
A. 8051에서 int 는 16비트(부호 있으면 -32768~32767)입니다. 큰 수는 uint32_t(또는 unsigned long)를 쓰세요.
Q. for (uint8_t i = 10; i >= 0; i--) 가 끝나지 않아요.
A. 부호 없는 i 는 음수가 되지 않습니다. 0에서 i-- 하면 -1이 아니라 255로 랩어라운드되어 i >= 0 이 영원히 참입니다. 카운트다운엔 부호 있는 타입을 쓰거나 조건을 다시 설계하세요.
Q. 레지스터에서 읽은 0xFF 를 변수에 담았더니 -1이 나와요.
A. 받는 변수가 int8_t/signed char 면 0xFF 는 2의 보수로 -1입니다. "비트 묶음"은 부호 없는 타입(uint8_t)으로 받아야 255로 읽힙니다.
정리
- 기본 자료형 크기는 칩마다 다르다(int: 8051 16비트, ARM/PC 32비트)
- stdint.h 의 uint8_t/uint16_t/uint32_t 는 어디서나 1/2/4바이트 — 임베디드 표준
- 레지스터·비트는 16진(0x)이 자연스럽고 선행 0(8진)에 주의한다
- sizeof 로 실제 바이트 수를 확인한다
- 부호 없는 값은 최댓값을 넘으면 0으로 랩어라운드된다
과제
- uint8_t 변수를 250부터 1씩 증가시키며 10회 출력해 255 다음 0으로 도는 것을 확인
- 0xA5 를 10진·16진·8진·2진으로 모두 출력하는 프로그램 작성
- sizeof(int) 와 sizeof(long) 을 PC gcc 로 찍어 본 뒤, 8051이라면 값이 어떻게 달라질지 표(핵심개념 1)와 비교