C 언어에서 포인터를 올바르게 이해하기

✍🏼 작성일 2019년 09월 16일   
❗️ 참고: 이 글이 작성된 지 이미 일이 지났습니다. 시의성에 유의하세요
🖥  설명:이 글은 C 언어를 배울 당시 작성한 블로그 포스트로, 오래된 WordPress 블로그에서 복원한 것입니다.

서문

C 언어를 배우는 초보자들은 종종 포인터에 대해 혼란스러워합니다. 아래에서는 제가 이해한 포인터에 대해 설명하겠습니다.

먼저 몇 가지 문제를 명확히 하기

변수의 본질

변수의 본질은 특정 메모리 주소를 가리키는 이름 표현입니다. 컴파일된 목적 코드에는 변수 이름이 없으며, 컴파일러는 컴파일 단계에서 변수 이름과 해당 메모리 주소 간의 매핑 테이블을 생성하여 변수 유형/이름/주소를 기록합니다.

변수를 선언할 때는 실제로 운영체제에 일정한 메모리 주소를 요청하는 것이며, 값을 할당할 때는 해당 주소에 적절한 데이터를 채우는 것입니다.

변수 할당의 본질

변수 x를 선언하고 주소가 0x0001이며, 이 주소에 1이 저장되어 있다고 가정합니다. x에 2를 할당하면 다음 단계가 수행됩니다:

  1. 매핑 테이블에서 변수 x의 주소를 조회하여 0x0001을 얻습니다.
  2. 값 2를 해당 주소에 저장하고 할당이 완료됩니다.

포인터 할당도 동일한 원리입니다.

*의 세 가지 의미

  1. 함수 프로토타입을 선언할 때, 예를 들어:
    1
    void fn(int *a)
    여기서 *a는 fn 함수가 포인터 변수(즉, 메모리 주소)를 매개변수로 받는다는 것을 나타내며, 이 매개변수가 a입니다.
  2. 함수 본문에서 *a가 등호 왼쪽에 나타날 때, 포인터 a가 가리키는 메모리 주소에 등호 오른쪽의 값을 저장한다는 것을 의미합니다. 예를 들어:
    1
    2
    3
    void fn(int *a) {
    *a = 3;
    }
  3. 함수 본문에서 *a가 등호 오른쪽에 나타날 때, 포인터 a가 가리키는 메모리 주소의 값을 가져온다는 것을 의미합니다.
    1
    2
    3
    void fn(int *a) {
    int tmp = *a;
    }

예시: 두 변수의 값을 교환하는 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
void swap(int *a, int *b);

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

void swap(int *a, int *b) {
int tmp = *a; // 也可以写作 int * tmp = *a; 只是说明 tmp 是一个指针变量, 即存储着指针的变量;
*a = *b;
*b = tmp;
}

예시 설명:

  1. main 함수에서 두 변수 x, y를 선언합니다. 주소는 &x와 &y이며, 이 주소에는 각각 값 1과 2가 저장되어 있습니다.
  2. swap 함수를 만나면, 이 함수는:
    1. 두 개의 포인터를 매개변수로 선언합니다. int *a 또는 int *b는 형식 매개변수로서 int 타입의 값을 가리키는 포인터로 위치할 수 있음을 나타내며, 이 함수는 int 타입 값을 가리키는 두 주소를 매개변수로 기대합니다. 이때 *는 포인터 변수를 받는다는 것을 나타냅니다.
  3. swap 함수를 실행하면:
    1. 형식 매개변수 a는 &x, 즉 x의 메모리 주소로 할당됩니다. 따라서 지역 변수 a의 메모리 주소에 저장된 값은 x의 메모리 주소입니다. 마찬가지로 형식 매개변수 b는 &y, 즉 y의 메모리 주소로 할당됩니다. (형식 매개변수에 *를 추가하는 것은 단지 이 매개변수가 포인터 타입임을 나타냅니다.)
    2. 지역 정수 변수 tmp를 선언하면, *a는 다음을 의미합니다:
      1. 첫 번째 단계: 메모리에서 a의 주소를 찾습니다.
      2. 두 번째 단계: a의 메모리 주소에 저장된 값을 읽습니다. 이 값은 또 다른 메모리 주소, 즉 변수 x의 메모리 주소입니다.
      3. 세 번째 단계: a의 값의 값을 반환합니다. 즉, a의 값은 메모리 주소이며, 이 메모리 주소의 값을 가져오면 x의 값인 1이 됩니다.
    3. _b는 변수 b의 값(메모리 주소)의 값(해당 메모리 주소에 저장된 값)을 _ 기호로 가져오는 것을 의미합니다. 즉, 메모리 주소 &y에 해당하는 값인 2를 가져온 후 *a, 즉 메모리 주소 &x에 저장된 값(a = &x이므로 x의 값인 1)에 할당합니다. swap 함수에서 main 함수의 변수 x의 메모리 주소에 저장된 값을 수정했음을 주의하세요.
    4. 마지막 문장에서 tmp는 값 1이며, 이를 *b에 할당합니다. 즉, b의 메모리 주소에 저장된 값인 2를 1로 덮어씁니다. 여기서 swap 함수는 포인터를 통해 main 함수의 y 메모리 주소에 저장된 값도 수정했습니다.

이해를 돕기 위해 그림을 첨부합니다:

交换值函数

交换值函数

- EOF -
이 글의 최초 게시: C 언어에서 포인터를 올바르게 이해하기 - Xheldon Blog