icon안동민 개발노트

포인터와 배열


포인터와 배열의 관계

 C++에서 배열과 포인터는 밀접한 관계를 가지고 있습니다.

 배열 이름은 배열의 첫 번째 요소를 가리키는 포인터로 취급됩니다.

#include <iostream>
 
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int* ptr = arr;  // arr은 &arr[0]와 동일
 
    std::cout << "arr: " << arr << std::endl;
    std::cout << "&arr[0]: " << &arr[0] << std::endl;
    std::cout << "ptr: " << ptr << std::endl;
 
    return 0;
}

 이 예제에서 arr, &arr[0], ptr는 모두 같은 주소를 가리킵니다.

배열 요소 접근

 포인터를 사용하여 배열 요소에 접근할 수 있습니다.

 이는 배열 표기법과 포인터 표기법 두 가지 방식으로 가능합니다.

#include <iostream>
 
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr = arr;
 
    for (int i = 0; i < 5; i++) {
        std::cout << "arr[" << i << "] = " << arr[i] << ", ";
        std::cout << "*(ptr + " << i << ") = " << *(ptr + i) << std::endl;
    }
 
    return 0;
}

포인터 연산

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

#include <iostream>
 
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
 
    std::cout << "*p = " << *p << std::endl;
    p++;  // 다음 정수의 주소로 이동 (4바이트 증가)
    std::cout << "*p = " << *p << std::endl;
    p += 2;  // 2개의 정수만큼 이동 (8바이트 증가)
    std::cout << "*p = " << *p << std::endl;
 
    return 0;
}

배열 decay

 함수에 배열을 전달할 때, 배열은 포인터로 "decay" 됩니다.

 이는 배열의 크기 정보가 손실됨을 의미합니다.

#include <iostream>
 
void printArray(int* arr, int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}
 
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printArray(arr, 5);  // arr은 포인터로 decay됨
 
    return 0;
}

포인터 vs 배열

 포인터와 배열은 유사하지만 중요한 차이점이 있습니다.

  1. 크기 정보 : 배열은 크기 정보를 가지고 있지만, 포인터는 그렇지 않습니다.
  2. 메모리 할당 : 배열은 스택에 할당되고, 포인터는 동적으로 힙에 할당될 수 있습니다.
  3. 재할당 : 배열은 재할당할 수 없지만, 포인터는 다른 주소를 가리키도록 변경할 수 있습니다.

다차원 배열과 포인터

 다차원 배열은 포인터의 배열로 표현할 수 있습니다.

#include <iostream>
 
int main() {
    int matrix[3][4] = {{1, 2, 3, 4},
                        {5, 6, 7, 8},
                        {9, 10, 11, 12}};
 
    int (*p)[4] = matrix;  // 4개의 정수 배열을 가리키는 포인터
 
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            std::cout << p[i][j] << " ";
        }
        std::cout << std::endl;
    }
 
    return 0;
}

문자열과 포인터

 C-스타일 문자열은 char 배열 또는 포인터로 표현됩니다.

#include <iostream>
#include <cstring>
 
int main() {
    char str[] = "Hello";
    char* pstr = "World";  // 주의: 상수 문자열
 
    std::cout << "str: " << str << std::endl;
    std::cout << "pstr: " << pstr << std::endl;
 
    // str[0] = 'h';  // 가능
    // pstr[0] = 'w';  // 불가능: 상수 문자열 수정 시도
 
    return 0;
}

동적 배열 할당

 포인터를 사용하여 동적으로 배열을 할당할 수 있습니다.

#include <iostream>
 
int main() {
    int size;
    std::cout << "배열 크기 입력: ";
    std::cin >> size;
 
    int* dynamicArr = new int[size];
 
    for (int i = 0; i < size; i++) {
        dynamicArr[i] = i * 10;
    }
 
    for (int i = 0; i < size; i++) {
        std::cout << dynamicArr[i] << " ";
    }
    std::cout << std::endl;
 
    delete[] dynamicArr;  // 메모리 해제 중요!
 
    return 0;
}

연습 문제

  1. 정수 배열을 역순으로 출력하는 함수를 포인터를 사용하여 구현하세요.
  2. 문자열을 복사하는 함수를 포인터를 사용하여 구현하세요. (strcpy 함수의 간단한 버전)
  3. 두 정수 배열을 입력받아 병합하는 함수를 작성하세요. 결과 배열은 동적으로 할당되어야 합니다.
  4. 포인터 배열을 사용하여 여러 개의 문자열을 저장하고 알파벳 순으로 정렬하는 프로그램을 작성하세요.

 참고자료

  • C++ 공식 문서의 배열 섹션 : Array declarations
  • "Effective C++" by Scott Meyers (항목 3 : 배열에 대해 신중히 대처하자)
  • "C++ Primer" by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo (Chapter 3 : Strings, Vectors, and Arrays)
  • C++ Core Guidelines의 배열 관련 규칙 : SL.con : Container Rules
  • "A Tour of C++" by Bjarne Stroustrup (Chapter 7 : Pointers, Arrays, and References)