일반적인 포인터 사용

a1.jpg

Understanding and Using C Pointers: Core Techniques for Memory Management 1st Edition을 읽고 정리한 내용을 기술합니다.

Understanding and Using C Pointers 정리 시리즈
  1. 포인터를 잘 알아야 하는 이유
  2. 포인터 선언하기
  3. Null의 개념
  4. void포인터와 전역, 정적포인터
  5. 포인터의 크기와 데이터 타입
  6. 일반적인 포인터 사용
  7. 동적 메모리 관리

일반적인 포인터 사용

포인터는 매우 다양한 방법으로 사용된다. 이번 포스팅에서는 다음을 포함한 포인터의 다양한 사용법에 대해서 다룬다.

  • 다중 수준 간접지정
  • 상수 포인터(중요)

다중 수준 간접지정

포인터는 다중 수준 간접지정(indirection)을 이용할 수 있다. 어떤 변수가 포인터에 대한 포인터로 선언된 경우를 흔하게 볼 수 있는데, 이를 이중 포인터(double pointer) 라고 부르기도 한다. 이중 포인터에 대한 좋은 예는 main 함수에 전통적인 argc와 argv 매개변수를 통해 프로그램 실행 인자가 전달되는 것이다. 이에 대해서는 추후 포스팅에서 다룬다.

예제

#include <stdio.h>

int main() {
    char* titles[] = {"A Tale of Two Cities",
                      "Wuthering Heights",
                      "Don Quixote",
                      "Odyssey",
                      "Moby-Dick",
                      "Hamlet",
                      "Gulliver's Travels"};
                      
    // bestBooks, englishBooks
    char **bestBooks[3];
    char **englishBooks[4];

    bestBooks[0] = &titles[0];
    bestBooks[1] = &titles[3];
    bestBooks[2] = &titles[5];

    englishBooks[0] = &titles[0];
    englishBooks[1] = &titles[1];
    englishBooks[2] = &titles[5];
    englishBooks[3] = &titles[6];

    printf("%s\n", *englishBooks[1]); // Wuthering Heights 출력
    return 0;
}

bestBooksenglishBooks배열은 titles 배열에서 책 제목을 복사하여 가지는 것보다 titles 배열에서 해당 책 제목의 주소를 포함하려고 한다.

이 방법의 결과 각각의 책 제목에 대한 메모리 복사를 피할 수 있으며 책 제목이 한 곳에만 위치하게 된다.

bestBooks[idx]englishBooks[idx]의 각 인덱스 주소공간에는 char*의 주소를 가지게 된다.

따라서 printf문에서는 해당 주소를 가진 포인터의 역참조(*)를 통해 책 제목을 가져올 수 있다.

상수와 포인터

영어 서적과 달리 한국에서는 상수 포인터, 포인터 상수 간의 혼용이 있는 것 같다…
일단 이번 포스팅에서는 포인터가 가리키는 주소에 있는 값을 변경하지 못하게 막는 것을 상수 포인터로, 포인터가 가리키는 주소를 변경하지 못하게 막는 것을 포인터 상수라고 하겠다.
본 서적에서는 각각을 상수에 대한 포인터, 상수 포인터로 칭한다. << 이렇게 외우는게 좋을 듯

예제 코드

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    
    int* ptr = arr; // HERE
    
    *ptr = 10;
    
    printf("%d ", *ptr);
    
    ptr++;
    
    printf("%d ", *ptr);
    
    return 0;
}
// 결과 : 10 2

위 코드에서 HERE 주석이 되어있는 line은 아무런 상수 표시가 되어있지 않다.
포인터가 가리키는 주소 : 바꿀 수 있음
포인터가 가리키는 주소의 값 : 바꿀 수 있음

상수 포인터의 경우

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    
    const int* ptr = arr; // 상수 포인터 : 포인터가 가리키는 주소의 값을 변경할 수 없음
    // int const *ptr = arr 도 똑같음!!!!!
    
    *ptr = 10; // error! : 포인터가 가리키는 주소의 값을 변경할 수 없음
    
    printf("%d ", *ptr);
    
    ptr++; // 주소 자체의 변경은 가능.
    
    printf("%d ", *ptr);
    
    return 0;
}

(상수(const) 이후에 포인터(*)가 오니 상수 포인터로 암기해도 될듯?)

포인터가 가리키는 주소 : 바꿀 수 있음
포인터가 가리키는 주소의 값 : 바꿀 수 없음

포인터 상수의 경우

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    
    int* const ptr = arr; // 포인터 상수 : 포인터가 가리키는 주소를 변경할 수 없음
    
    *ptr = 10; // 주소가 가리키는 값의 변경은 가능.
    
    printf("%d ", *ptr);
    
    ptr++; // error! : 주소 자체의 변경 불가능!!
    
    printf("%d ", *ptr);
    
    return 0;
}

포인터가 가리키는 주소 : 바꿀 수 없음
포인터가 가리키는 주소의 값 : 바꿀 수 있음

둘 다인 경우

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    
    const int* const ptr = arr; // 상수 포인터 : 포인터가 가리키는 주소는 물론 주소의 값도 변경할 수 없음
    
    *ptr = 10; // error! : 포인터가 가리키는 주소의 값을 변경할 수 없음
    
    printf("%d ", *ptr);
    
    ptr++; // error! : 포인터가 가리키는 주소를 변경할 수 없음
    
    printf("%d ", *ptr);
    
    return 0;
}

포인터가 가리키는 주소 : 바꿀 수 없음
포인터가 가리키는 주소의 값 : 바꿀 수 없음