포인터 개념
포인터는 프로그래밍에서 매우 중요한 개념으로, 메모리 주소를 저장하는 변수입니다. 일반 변수는 데이터를 직접 저장하지만, 포인터 변수는 데이터가 저장된 메모리의 위치, 즉 주소값을 저장합니다. 따라서 포인터를 사용하면 변수의 실제 메모리 위치를 참조하거나 조작할 수 있습니다. 포인터는 메모리를 효율적으로 관리하고, 함수 간 변수 값을 전달하거나 배열과 문자열을 다룰 때 매우 유용합니다.
포인터는 다양한 자료형에 대해 선언할 수 있으며, 포인터가 가리키는 데이터 타입에 따라 메모리를 해석하는 방식이 달라집니다. 예를 들어 int*는 정수형 변수를 가리키는 포인터, char*는 문자형 변수를 가리키는 포인터입니다. 포인터 자체도 메모리를 차지하며, 시스템 아키텍처에 따라 보통 4바이트(32비트) 또는 8바이트(64비트)를 사용합니다.
포인터 선언과 초기화
포인터를 선언하는 기본 문법은 다음과 같습니다:
int *ptr; // int형 데이터를 가리키는 포인터 변수
위 코드는 ptr이라는 포인터 변수를 선언한 것이며, 이는 int형 데이터의 주소를 저장할 수 있습니다. 그러나 이 상태로는 어떤 주소도 가리키지 않으므로 초기화 후 사용하는 것이 중요합니다.
포인터 초기화는 변수의 주소를 할당하는 방식입니다:
int num = 10;
int *ptr = # // num 변수의 주소를 ptr에 저장
이제 ptr은 num의 메모리 주소를 가리키게 되며, 역참조 연산자를 통해 num의 값을 간접적으로 읽거나 변경할 수 있습니다.
포인터와 변수의 관계
포인터는 변수의 메모리 주소를 저장하므로 변수와 밀접한 관계가 있습니다. 포인터를 통해 변수의 값을 직접 수정하거나, 함수에 주소를 전달하여 원본 데이터를 조작할 수 있습니다.
메모리 주소 연산자 (&)
& 연산자는 변수 앞에 붙여 그 변수의 메모리 주소를 얻습니다.
int num = 10;
int *ptr = # // num 변수의 주소를 ptr에 저장
printf("num의 주소: %p\n", &num);
역참조 연산자 (*)
* 연산자는 포인터가 가리키는 메모리 공간에 접근(읽기/쓰기)할 수 있게 해 줍니다.
int num = 10;
int *ptr = # // num의 주소를 ptr에 저장
printf("ptr이 가리키는 값: %d\n", *ptr); // 10 출력
*ptr = 20; // num의 값을 20으로 변경
printf("num의 값: %d\n", num); // 20 출력
*ptr은 ptr이 가리키는 주소의 값을 의미하므로, 이를 변경하면 원본 num의 값도 변경됩니다.
포인터와 메모리
컴퓨터의 메모리는 연속된 바이트 공간으로 구성되며, 각 공간은 고유한 주소를 가집니다. 변수는 메모리 공간을 점유하고, 포인터는 그 공간의 주소를 저장합니다.
포인터를 사용하면 동적 메모리 할당, 배열 처리, 다양한 자료구조 구현이 가능해지며, 배열과 포인터는 특히 밀접한 관계가 있습니다.
배열과 포인터
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]); // 또는 *(ptr + i)
}
배열 이름 arr은 배열의 첫 번째 요소의 주소와 같으며, ptr[i] 또는 *(ptr + i)로 배열 요소에 접근할 수 있습니다.
포인터 산술 연산
포인터는 + / - 연산을 통해 메모리 내 위치를 이동할 수 있습니다.
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("%d\n", *(ptr + 2)); // arr[2] 값 출력 → 30
이처럼 포인터 산술은 배열 순회나 동적 자료구조 구현에 자주 사용됩니다.
포인터와 함수
포인터는 함수 인자로 자주 사용되며, 이를 통해 원본 변수에 직접 접근하거나 큰 데이터를 복사하지 않고 처리할 수 있어 효율적입니다.
포인터를 이용한 함수 인자 전달
void changeValue(int *p) {
*p = 100;
}
int main() {
int num = 10;
changeValue(&num);
printf("num의 값: %d\n", num); // 100 출력
return 0;
}
함수에 포인터를 넘겨줌으로써 함수 내부에서 실제 변수 값을 변경할 수 있습니다.
NULL 포인터와 포인터 초기화
초기화되지 않은 포인터는 쓰레기 값을 가지므로, NULL 또는 C++에서는 nullptr로 초기화하는 것이 좋습니다.
int *ptr = NULL; // 유효한 주소가 없음을 명시
포인터를 사용할 때는 항상 NULL 여부를 검사한 후 역참조해야 안정적인 프로그램이 됩니다.
포인터 사용 시 주의사항
- 초기화되지 않은 포인터 사용 금지: 반드시 유효한 주소나 NULL로 초기화하세요.
- 잘못된 주소 접근 금지: 해제된 메모리나 무효한 주소 접근은 프로그램 오류를 유발합니다.
- 포인터 산술 조심: 배열 범위를 초과하면 예기치 않은 동작이 발생할 수 있습니다.
- 메모리 해제 필수:
malloc또는new로 할당한 메모리는free또는delete로 반드시 해제하세요. - NULL 검사 습관화: 역참조 전에 항상 NULL 여부를 확인하세요.
요약 및 결론
포인터는 메모리 주소를 저장하는 변수로, 변수와 함수의 값을 직접 조작할 수 있는 중요한 도구입니다. 배열, 문자열, 구조체, 동적 메모리 등 다양한 분야에서 필수적으로 사용됩니다.
포인터를 잘 사용하려면 &, * 연산자의 의미와 포인터 산술, 함수 인자 전달 방식 등을 정확히 이해해야 하며, 초기화, NULL 체크, 메모리 해제 등의 관리도 철저히 해야 합니다.
이 글을 통해 포인터의 기본 개념부터 활용법까지 체계적으로 익혀 C 언어 프로그래밍 실력을 한 단계 업그레이드하시길 바랍니다.
'Programming' 카테고리의 다른 글
| C 구조체와 공용체 (60) | 2025.08.18 |
|---|---|
| C 파일 입출력 (53) | 2025.08.17 |
| C 배열과 문자열 (36) | 2025.08.15 |
| C 함수 (63) | 2025.08.14 |
| C 다차원 배열과 포인터 (62) | 2025.08.13 |