임베디드 스터디 - 데이터 타입과 메모리 레이아웃
임베디드 스터디 - 데이터 타입과 메모리 레이아웃
이번 글 참고자료:
EmbeddedInterviewlab
메모리 맵
- C의 변수는 메모리에서 5개의 구역 중 하나에 배치된다.
- stack : 함수 내 지역 변수
- heap : 동적할당 변수
- .rodata :
const전역 변수,const static지역 변수, flash 메모리에 저장 - .data : 초기화된 전역 변수, 프로그램 실행 시 flash 메모리에서 RAM으로 초기화값을 복사한다.
- .bss : 초기화되지 않은 전역 변수,
static지역 변수, RAM에서 0 값으로 초기화된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// .rodata (flash) -- const + global scope = read-only data in flash
const uint32_t FIRMWARE_VERSION = 0x0102;
// .data (RAM, copied from flash at boot) -- initialized global
uint32_t sensor_count = 5;
// .bss (RAM, zeroed at boot) -- uninitialized global (zero by default)
uint32_t error_count;
// .data -- static global, same as non-static for memory purposes
static uint32_t module_id = 42;
// .bss -- uninitialized static global
static uint32_t call_count;
void read_sensor(uint16_t channel) {
// Stack -- automatic storage, NOT initialized, destroyed on return
uint8_t buffer[64];
// .bss -- static LOCAL, persists between calls, zero-initialized once
static uint32_t invocation_count;
invocation_count++;
// .rodata -- static const local, stored in flash (no RAM cost)
static const uint16_t lookup[] = {0, 100, 200, 400, 800};
}
- C로 개발한 프로그램은 링커 스크립트의 내용에 따라 변수의 저장 위치가 달라진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
FLASH RAM ┌─────────────────────┐ ┌─────────────────────┐ ← _estack │ │ │ Stack ↓ │ (initial SP) │ .text │ │ local vars, args, │ │ (machine code: │ │ return addresses │ │ main, ISRs) │ │ ... │ │ │ │ │ ├─────────────────────┤ │ (free space) │ │ .rodata │ │ │ │ (const globals, │ │ ... │ │ string literals, │ │ │ │ lookup tables) │ │ Heap ↑ │ ├─────────────────────┤ │ malloc'd memory │ │ .data init values │ ──copy at──▶ ├─────────────────────┤ │ (source for RAM │ boot │ .bss │ │ .data section) │ │ (zeroed at boot) │ └─────────────────────┘ ├─────────────────────┤ 0x0800_0000 (STM32) │ .data │ │ (copied from flash)│ └─────────────────────┘ 0x2000_0000 (STM32)
Int 변수 선언
- 각 CPU의 스펙에 따라 int, long의 크기 달라지므로, 명확하게 데이터의 크기를 지정해야한다.
stdint.h라이브러리를 통해 각 데이터의 명확한 크기를 정의하여 변수를 사용한다.
| Type | Bits | Range (unsigned) | Range (signed) | Use Case |
|---|---|---|---|---|
| uint8_t | 8 | 0 - 255 | -128 to 127 | Register bytes, flags, small counters |
| uint16_t | 16 | 0 - 65,535 | -32,768 to 32,767 | ADC values, sensor data, PWM |
| uint32_t | 32 | 0 - 4.29B | -2.14B to 2.14B | Timestamps, addresses, large counters |
| uint64_t | 64 | 0 - 18.4E | -9.2E to 9.2E | Cryptography, precise time (avoid on 8/16-bit MCUs) |
- Implicit signed/unsigned conversion (usual arithmetic conversions)으로 인해 Int와 Uint 사이에 비교 연산자를 사용하는 경우 결과가 의도하지 않은 형태로 발생한다.(Int의 -1은 Uint의 UINT_MAX 값이 나온다.)
- 비교 연산자 사용 시, Unsigned 끼리, Signed 끼리만 비교한다.
- 변수 타입 변환 시 해당 변수에 명시한다.
if ((int)y > x) - 컴파일러의 경고를 활용한다.
-Wsign-compare,-Wconversion - MISRA C에서는 변수 타입 변환 시 모두 명시할 것을 요구하고 있다.
Stack vs Static vs Heap
- Stack, Static, Heap 메모리는 각각의 용도에 맞춰 사용하는 것이 효율적이다.
- Static은 보통 100Byte 이상의 데이터를 저장하는게 좋다.
- Stack에는 작은 지역 변수나 함수 호출 오버헤드를 위해 남겨두는 것이 좋다.
- Heap은 메모리 파편화, Stack-Heap 충돌 문제를 일으킬 수 있으므로, 특수 상황이 아니면 안 쓰는 것이 오류를 피하는 지름길이다.
| Criteria | Stack (auto) | Static / Global | Heap (malloc) |
|---|---|---|---|
| Lifetime | Function scope only | Entire program | Until free() |
| Initialization | NOT initialized | .bss zeroed, .data from flash | NOT initialized |
| Size limit | Small (1-8 KB total) | Limited by RAM | Limited by RAM |
| Allocation speed | Instant (just move SP) | Zero (placed at link time) | Slow (search free list) |
| Deterministic | Yes | Yes | No (fragmentation) |
| Reentrant | Yes | No (shared state) | Yes (if per-thread) |
| Best for | Small temps, loop vars | Config, LUTs, buffers, state | Rarely used in embedded |
프로그램 스타트업 절차
- 디바이스는 아래 절차를 통해 프로그램을 실행한다.
- Power on : CPU가 실행되면 reset vector에서부터 메모리를 읽는다.
Reset_Handler: 스택 포인터를 초기화하고, C runtime initialization을 수행한다..data복사 : 초기화된 전역, static 변수의 데이터를 flash에서 RAM으로 복사한다..bss초기화 :.bss섹션을 모두 0으로 초기화한다.main()함수 실행
- 함수의 지역 변수는 함수가 호출된 후에 생성되므로 변수 초기화가 자동으로 이루어지지 않는다.
.data섹션의 데이터량과 부팅시간은 비례한다.- 따라서 적절하게 변수를 선언하여
.data섹션을 사용하는 것이 중요하다.
- 따라서 적절하게 변수를 선언하여
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.