포인터의 기본

2025. 3. 28. 08:43Programming Languages/C++

6.2 포인터의 기본

6.2.1 포인터란?

포인터는 메모리 주소를 저장하는 변수입니다. 즉, 다른 변수나 데이터를 "가리키는" 변수입니다.

#include <iostream>

int main() {
    int num = 42;        // 일반 변수
    int* ptr = &num;     // 포인터 변수 (num의 주소를 저장)
    
    std::cout << "num의 값: " << num << std::endl;
    std::cout << "num의 주소: " << &num << std::endl;
    std::cout << "ptr의 값(num의 주소): " << ptr << std::endl;
    std::cout << "ptr이 가리키는 값: " << *ptr << std::endl;
    
    return 0;
}

위 코드에서:

  • int* ptr: 정수를 가리키는 포인터 변수 선언
  • &num: num 변수의 주소를 반환 (주소 연산자)
  • *ptr: ptr이 가리키는 주소에 있는 값을 반환 (역참조 연산자)

6.2.2 포인터 선언과 초기화

포인터는 타입* 변수명 형식으로 선언합니다:

int* p1;       // 정수 포인터 (권장 스타일)
int *p2;       // 정수 포인터 (C 스타일)
int * p3;      // 정수 포인터 (공백 스타일)

int* p4, p5;   // p4는 포인터, p5는 일반 int (!주의!)
int *p6, *p7;  // p6과 p7 모두 포인터

포인터 초기화:

#include <iostream>

int main() {
    // 변수 주소로 초기화
    int num = 42;
    int* ptr1 = &num;
    
    // null 포인터로 초기화
    int* ptr2 = nullptr;  // C++11 이후 권장
    int* ptr3 = NULL;     // C 스타일 (매크로)
    int* ptr4 = 0;        // 0으로 초기화
    
    // 다른 포인터 값으로 초기화
    int* ptr5 = ptr1;
    
    // 초기화되지 않은 포인터 (위험!)
    int* ptr6;
    
    // 안전한 사용 예시
    if (ptr2 == nullptr) {
        std::cout << "ptr2는 null 포인터입니다." << std::endl;
    }
    
    return 0;
}

6.2.3 포인터 역참조

역참조 연산자(*)를 사용하여 포인터가 가리키는 메모리 위치의 값에 접근할 수 있습니다:

#include <iostream>

int main() {
    int num = 42;
    int* ptr = &num;
    
    // 역참조로 값 읽기
    std::cout << "역참조로 값 읽기: " << *ptr << std::endl;
    
    // 역참조로 값 변경
    *ptr = 100;
    std::cout << "ptr을 통해 값 변경 후: " << num << std::endl;
    
    // 원본 변수 값 변경
    num = 200;
    std::cout << "원본 변수 변경 후 역참조: " << *ptr << std::endl;
    
    return 0;
}

주의: 초기화되지 않았거나 null인 포인터를 역참조하면 프로그램이 충돌할 수 있습니다!

int* nullPtr = nullptr;
*nullPtr = 42;  // 오류: 널 포인터 역참조

6.2.4. void 포인터

void* 포인터는 어떤 타입의 데이터도 가리킬 수 있는 일반 포인터입니다:

#include <iostream>

int main() {
    int num = 42;
    double pi = 3.14159;
    char letter = 'A';
    
    // void 포인터로 여러 타입 가리키기
    void* voidPtr;
    
    voidPtr = &num;
    std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
    
    voidPtr = &pi;
    std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
    
    voidPtr = &letter;
    std::cout << "voidPtr이 가리키는 주소: " << voidPtr << std::endl;
    
    // void 포인터를 역참조하려면 명시적 타입 변환 필요
    // *voidPtr = 100;  // 오류: void 포인터 역참조 불가
    
    // 올바른 사용법: 캐스팅 후 역참조
    voidPtr = &num;
    *static_cast<int*>(voidPtr) = 100;
    std::cout << "num의 값: " << num << std::endl;
    
    return 0;
}

6.2.5 포인터 연산

포인터에 정수를 더하거나 빼면 해당 타입의 크기에 맞게 주소가 변경됩니다:

#include <iostream>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    int* ptr = numbers;  // 배열 이름은 첫 번째 요소의 주소
    
    std::cout << "배열 주소: " << numbers << std::endl;
    std::cout << "ptr이 가리키는 주소: " << ptr << std::endl;
    
    // 포인터 증가
    ptr++;
    std::cout << "ptr++ 후 주소: " << ptr << std::endl;
    std::cout << "ptr이 가리키는 값: " << *ptr << std::endl;  // 20
    
    // 포인터에 정수 더하기
    ptr = numbers;  // 다시 첫 번째 요소로 초기화
    ptr = ptr + 2;  // 두 번째 요소 건너뛰기
    std::cout << "ptr + 2 후 주소: " << ptr << std::endl;
    std::cout << "ptr이 가리키는 값: " << *ptr << std::endl;  // 30
    
    // 역참조와 포인터 연산 결합
    ptr = numbers;  // 다시 첫 번째 요소로 초기화
    std::cout << "*(ptr + 3): " << *(ptr + 3) << std::endl;  // 40
    
    // 포인터 뺄셈
    ptr = &numbers[4];  // 마지막 요소의 주소
    ptr -= 2;  // 두 요소 뒤로
    std::cout << "ptr -= 2 후 가리키는 값: " << *ptr << std::endl;  // 30
    
    return 0;
}

6.2.6 포인터와 배열의 관계

C++에서 배열 이름은 배열의 첫 번째 요소를 가리키는 포인터로 취급될 수 있습니다:

#include <iostream>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    
    // 배열 이름을 포인터처럼 사용
    std::cout << "numbers[0]: " << numbers[0] << std::endl;
    std::cout << "*numbers: " << *numbers << std::endl;  // 같은 결과
    
    std::cout << "numbers[1]: " << numbers[1] << std::endl;
    std::cout << "*(numbers + 1): " << *(numbers + 1) << std::endl;  // 같은 결과
    
    // 포인터 표기법으로 배열 순회
    int* ptr = numbers;
    for (int i = 0; i < 5; i++) {
        std::cout << "Element " << i << ": " << *(ptr + i) << std::endl;
    }
    
    // 또는
    ptr = numbers;
    for (int i = 0; i < 5; i++) {
        std::cout << "Element " << i << ": " << *ptr << std::endl;
        ptr++;
    }
    
    return 0;
}

 

배열과 포인터의 중요한 차이점:

  • 배열은 연속된 메모리 블록으로, 크기가 고정되어 있습니다.
  • 배열 이름은 상수 포인터로 취급되므로 다른 주소를 가리키도록 변경할 수 없습니다 (numbers = someOtherArray; 불가능).
  • sizeof(배열)은 전체 배열 크기를 반환하지만, sizeof(포인터)는 포인터 변수 자체의 크기를 반환합니다.

 

'Programming Languages > C++' 카테고리의 다른 글

참조  (0) 2025.03.28
동적 메모리 할당  (0) 2025.03.28
포인터와 참조  (0) 2025.03.28
챕터5. 실습 문제  (0) 2025.03.28
다형성(Polymorphism)  (0) 2025.03.28