이번 글에서는 C언어에서 변수가 메모리에 어떤 형태로 저장되어 있는지, 즉 메모리 표현을 직접 출력해 보는 방법을 알아보겠습니다. 정수(int), 실수(float), 그리고 포인터(pointer) 세 가지 자료형을 예로 들어 설명합니다.
구현 원리
변수의 메모리 내용을 바이트 단위로 확인하려면 다음과 같은 순서로 진행합니다.
- 출력할 변수의 주소와 크기(sizeof)를 구합니다.
- 해당 주소를
unsigned char*타입으로 캐스팅하여 바이트 단위로 접근할 수 있게 만듭니다. - 변수의 크기만큼 반복문을 돌면서 캐스팅된 포인터가 가리키는 각 바이트의 값을 16진수로 출력합니다.
char 포인터를 사용하는 이유는 char가 정확히 1바이트 크기를 가지기 때문입니다. 어떤 자료형의 주소라도 char 포인터로 변환하면 메모리를 바이트 단위로 하나씩 읽어낼 수 있습니다.
예제 코드
#include <stdio.h>
typedef unsigned char *byte_pointer; // char*를 이용해 바이트 포인터 생성
void disp_bytes(byte_pointer ptr, int len) {
// 바이트 포인터를 받아 메모리 내용을 출력
int i;
for (i = 0; i < len; i++)
printf(" %.2x", ptr[i]);
printf("\n");
}
void disp_int(int x) {
disp_bytes((byte_pointer) &x, sizeof(int));
}
void disp_float(float x) {
disp_bytes((byte_pointer) &x, sizeof(float));
}
void disp_pointer(void *x) {
disp_bytes((byte_pointer) &x, sizeof(void *));
}
main() {
int i = 5;
float f = 2.0;
int *p = &i;
disp_int(i);
disp_float(f);
disp_pointer(p);
disp_int(i);
}
실행 결과
05 00 00 00
00 00 00 40
3c fe 22 00 00 00 00 00
05 00 00 00
결과 해석
정수 5의 경우: 05 00 00 00으로 출력됩니다. 이는 리틀 엔디안(Little Endian) 방식으로, 최하위 바이트(05)가 낮은 주소에 먼저 저장되기 때문입니다.
실수 2.0의 경우: 00 00 00 40으로 출력됩니다. IEEE 754 부동소수점 표준에 따라 2.0은 지수부와 가수부로 인코딩되며, 역시 리틀 엔디안 순서로 저장됩니다.
포인터의 경우: 실행 환경마다 할당되는 주소가 다르므로 출력 값은 시스템과 실행 시점에 따라 달라질 수 있습니다. 위 예제에서는 8바이트 크기의 포인터가 출력되었는데, 이는 64비트 시스템임을 의미합니다.
마지막 줄의 disp_int(i)는 동일한 변수를 다시 출력한 것으로, 같은 결과 05 00 00 00이 나오는 것을 확인할 수 있습니다.