포인터와 배열
포인터와 배열의 관계
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 배열
포인터와 배열은 유사하지만 중요한 차이점이 있습니다.
- 크기 정보 : 배열은 크기 정보를 가지고 있지만, 포인터는 그렇지 않습니다.
- 메모리 할당 : 배열은 스택에 할당되고, 포인터는 동적으로 힙에 할당될 수 있습니다.
- 재할당 : 배열은 재할당할 수 없지만, 포인터는 다른 주소를 가리키도록 변경할 수 있습니다.
다차원 배열과 포인터
다차원 배열은 포인터의 배열로 표현할 수 있습니다.
#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;
}
연습 문제
- 정수 배열을 역순으로 출력하는 함수를 포인터를 사용하여 구현하세요.
- 문자열을 복사하는 함수를 포인터를 사용하여 구현하세요. (strcpy 함수의 간단한 버전)
- 두 정수 배열을 입력받아 병합하는 함수를 작성하세요. 결과 배열은 동적으로 할당되어야 합니다.
- 포인터 배열을 사용하여 여러 개의 문자열을 저장하고 알파벳 순으로 정렬하는 프로그램을 작성하세요.
참고자료
- 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)