security
CHAPTER 88 / 96
읽기 약 2분
FUNCTION
포인터 연산: 주소 계산의 수학
핵심 개념
포인터 덧셈·뺄셈 — arr[i] == *(arr+i). sizeof와 메모리 레이아웃 시각화.
본문
포인터 산술
#include <stdio.h>
int main(void) {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
// 포인터 + 정수
printf("%d\n", *p); // 10 (arr[0])
printf("%d\n", *(p+1)); // 20 (arr[1])
printf("%d\n", *(p+2)); // 30 (arr[2])
// 사실 arr[i]는 *(arr+i)의 syntactic sugar
printf("%d\n", arr[3]); // 40
printf("%d\n", *(arr+3)); // 40 — 동일
return 0;
}주소 계산 — 타입 크기 만큼
#include <stdio.h>
int main(void) {
int iarr[3];
char carr[3];
double darr[3];
int *pi = iarr;
char *pc = carr;
double *pd = darr;
printf("pi = %p\n", (void*)pi);
printf("pi+1 = %p\n", (void*)(pi+1)); // +4 (sizeof int)
printf("pc = %p\n", (void*)pc);
printf("pc+1 = %p\n", (void*)(pc+1)); // +1 (sizeof char)
printf("pd = %p\n", (void*)pd);
printf("pd+1 = %p\n", (void*)(pd+1)); // +8 (sizeof double)
// 포인터+1 = 주소 + sizeof(타입)
return 0;
}포인터 차이
#include <stdio.h>
int main(void) {
int arr[10];
int *p1 = &arr[2];
int *p2 = &arr[7];
ptrdiff_t diff = p2 - p1; // 5 (원소 단위)
printf("diff = %td\n", diff);
// 바이트 차이 — 포인터를 캐스팅
printf("byte diff = %td\n",
(char*)p2 - (char*)p1); // 20
return 0;
}sizeof 연산자
#include <stdio.h>
struct Point {
int x;
int y;
double z;
};
int main(void) {
int i = 0;
double d = 0;
char arr[10];
struct Point pt;
int *p = NULL;
printf("sizeof(int) = %zu\n", sizeof(int)); // 4
printf("sizeof(double) = %zu\n", sizeof(double)); // 8
printf("sizeof(char) = %zu\n", sizeof(char)); // 1
printf("sizeof(arr) = %zu\n", sizeof(arr)); // 10
printf("sizeof(struct Point) = %zu\n", sizeof(pt)); // 16 or padding 적용
printf("sizeof(int*) = %zu\n", sizeof(p)); // 8 (64-bit)
// 배열 길이 매크로
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
printf("ARRAY_LEN(arr) = %zu\n", ARRAY_LEN(arr)); // 10
return 0;
}메모리 레이아웃
#include <stdio.h>
int main(void) {
int arr[5] = {10, 20, 30, 40, 50};
printf("주소 값 인덱스\n");
for (int i = 0; i < 5; i++) {
printf("%p %d arr[%d]\n",
(void*)&arr[i], arr[i], i);
}
// 결과 (예시):
// 0x7ffd1234 10 arr[0]
// 0x7ffd1238 20 arr[1] ← +4 바이트
// 0x7ffd123c 30 arr[2]
// 0x7ffd1240 40 arr[3]
// 0x7ffd1244 50 arr[4]
return 0;
}배열 순회 — 4가지 스타일
#include <stdio.h>
int main(void) {
int arr[5] = {10, 20, 30, 40, 50};
size_t n = sizeof(arr) / sizeof(arr[0]);
// 스타일 1: 인덱스
for (size_t i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 스타일 2: 포인터 산술
int *p;
for (p = arr; p < arr + n; p++) {
printf("%d ", *p);
}
printf("\n");
// 스타일 3: 인덱스 + 포인터 시각화
for (size_t i = 0; i < n; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
// 스타일 4: 포인터를 인덱스처럼
p = arr;
for (size_t i = 0; i < n; i++) {
printf("%d ", p[i]);
}
printf("\n");
return 0;
}포인터 산술의 한계
#include <stdio.h>
int main(void) {
int arr[5];
// ❌ 다른 배열 사이의 포인터 차이 — 정의되지 않음
int other[3];
// ptrdiff_t bad = arr - other; // 정의되지 않은 동작
// ❌ 배열 끝 너머 포인터 — 1단계만 정의됨
int *end = arr + 5; // OK (one-past-the-end)
// int *bad = arr + 10; // 정의되지 않음 (역참조 시 위험)
// ⚠️ 음수 인덱스 — 정의되지 않음 (단, 의도적 트릭은 가능)
int *p = &arr[2];
printf("%d\n", p[-2]); // arr[0] (정의된 동작)
// p[-3] // ⚠️ 배열 시작 전 — 정의되지 않음
return 0;
}다음 챕터
CH.5 "스택과 힙" — 메모리 영역 이해.
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 C 코드의 배열·포인터 산술 위치를 분석해서 off-by-one 오류 가능성과 안전한 패턴을 추천해줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
한국 임베디드 개발자의 메모리 레이아웃 디버깅 사례 5가지를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 C 코드베이스에서 포인터 산술이 복잡한 위치 Top 10을 분석해서 리팩토링 가이드를 만들어줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 C/C++ 메모리 안전 도구 (ASan/MSan/Valgrind)와 실무 활용을 솔직히 알려줘.
⭐ 이것만 기억하세요
포인터 연산: 주소 계산의 수학은 이 3가지만 확실히 잡으세요
1.포인터 + 1은 주소 + sizeof(타입) — 타입에 따라 점프 크기 다름
2.arr[i]는 *(arr+i)의 syntactic sugar — C에서 배열과 포인터는 동전의 양면
3.다음 챕터 CH.5에서 스택과 힙 — 메모리가 어디에 어떻게 저장되는가
공유하기
진행도 88 / 96