OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 87 / 96
security
CHAPTER 87 / 96
읽기 약 2
FUNCTION

포인터 기초: 메모리 주소의 세계


핵심 개념

포인터 선언(*)·주소 연산자(&)·역참조 — 메모리 주소가 무엇인가. swap 함수로 포인터 이해.

본문

포인터 = 주소를 담는 변수

C📋 코드 (15줄)
#include <stdio.h>

int main(void) {
    int x = 42;
    int *p = &x;  // p는 x의 주소를 저장

    printf("x = %d\n", x);          // 42
    printf("&x = %p\n", (void*)&x); // x의 주소 (16진수)
    printf("p = %p\n", (void*)p);   // p의 값 = x의 주소
    printf("*p = %d\n", *p);        // p가 가리키는 곳의 값 = 42

    *p = 100;  // 역참조해서 값 변경
    printf("x = %d\n", x);  // 100 (x도 같이 변경됨)
    return 0;
}

메모리 주소 시각화

📋 코드 (6줄)
변수 x:  값 42  주소 0x7ffe1234
변수 p:  값 0x7ffe1234  주소 0x7ffe1238
                ↓
        x를 가리킴

*p = 역참조 = "p가 가리키는 곳의 값"

포인터 종류

C📋 코드 (18줄)
#include <stdio.h>

int main(void) {
    int    i = 10;      int    *pi = &i;
    char   c = 'A';     char   *pc = &c;
    double d = 3.14;    double *pd = &d;

    // 각 포인터는 자기 타입의 주소만 저장
    // int* 에 char 주소 저장 — 경고/에러

    printf("int*  size: %zu\n", sizeof(pi));  // 8 (64-bit)
    printf("char* size: %zu\n", sizeof(pc));  // 8
    printf("double* size: %zu\n", sizeof(pd)); // 8
    // 포인터 자체 크기는 모두 동일 (메모리 주소 길이)
    // 단, 가리키는 데이터 크기는 다름

    return 0;
}

NULL 포인터

C📋 코드 (18줄)
#include <stdio.h>

int main(void) {
    int *p = NULL;  // 아무것도 안 가리킴

    if (p == NULL) {
        printf("p is null\n");
    }

    // ❌ NULL 역참조 — Segmentation fault
    // *p = 10;

    // ✅ 항상 NULL 검사
    if (p != NULL) {
        *p = 10;
    }
    return 0;
}

swap 함수 — 포인터의 필요성

C📋 코드 (27줄)
#include <stdio.h>

// ❌ 값 전달 — 변경 안 됨
void swap_bad(int a, int b) {
    int tmp = a;
    a = b;
    b = tmp;
    // 함수 안의 a, b만 바뀜 — 호출자 변수 무관
}

// ✅ 포인터 전달 — 원본 변경
void swap_good(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(void) {
    int x = 1, y = 2;

    swap_bad(x, y);
    printf("bad: x=%d y=%d\n", x, y);  // 1, 2 (변경 안 됨)

    swap_good(&x, &y);
    printf("good: x=%d y=%d\n", x, y);  // 2, 1 (변경됨)
    return 0;
}

배열 = 포인터?

C📋 코드 (26줄)
#include <stdio.h>

int main(void) {
    int arr[3] = {10, 20, 30};

    // arr 자체는 첫 원소의 주소
    printf("arr     = %p\n", (void*)arr);
    printf("&arr[0] = %p\n", (void*)&arr[0]);
    // 같은 주소

    // 그러나 sizeof는 다름
    printf("sizeof(arr) = %zu\n", sizeof(arr));    // 12 (배열 전체)
    int *p = arr;
    printf("sizeof(p)   = %zu\n", sizeof(p));      // 8 (포인터)
    // 함수에 배열 전달 시 — 포인터로 변환됨 (sizeof 정보 손실!)
    return 0;
}


// 함수 인자 — 배열 size를 명시적으로 전달
void print_array(int *arr, size_t len) {
    for (size_t i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

포인터 함정

C📋 코드 (12줄)
#include <stdio.h>

int *bad_function(void) {
    int local = 42;  // 지역 변수 — 함수 종료 시 사라짐
    return &local;   // ⚠️ dangling 포인터 반환
}

int main(void) {
    int *p = bad_function();
    // *p = ?  // 정의되지 않은 동작
    return 0;
}

다음 챕터

CH.4 "포인터 연산" — 주소 계산의 수학.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 C 코드의 포인터 사용을 분석해서
NULL 검사 누락 + dangling pointer
위험 위치를 보고해줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

한국 시스템 프로그래머가 자주 만나는
포인터 함정 Top 10과 디버깅 팁을 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 코드베이스 전체에서 포인터 안전성을
자동 분석해서 우선 수정 위치를 만들어줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 Rust의 차용 검사기 vs C의 수동
메모리 관리, 학습 곡선 차이를 솔직히 알려줘.

⭐ 이것만 기억하세요
포인터 기초: 메모리 주소의 세계 이 3가지만 확실히 잡으세요
1.포인터는 주소를 담는 변수 — &로 주소 얻고, *로 역참조
2.swap·배열·문자열 등 함수에 데이터를 전달할 때 포인터가 필수 (값 전달은 복사만)
3.다음 챕터 CH.4에서 포인터 연산 — 배열과 포인터의 깊은 관계


공유하기
진행도 87 / 96