
9장 포인터 완벽 정리와 핵심 개념
포인터는 C언어의 핵심이자 강력한 기능 중 하나입니다. 포인터를 잘 활용하면 메모리 직접 제어가 가능해져 효율적인 프로그래밍이 가능하지만, 그만큼 조심하지 않으면 프로그램 오류의 원인이 되기도 합니다. 이번 글에서는 포인터의 기본 개념부터, 형변환, 함수 포인터, 그리고 주의할 점까지 꼼꼼하게 정리해 보겠습니다.
1. 포인터란 무엇인가?
포인터는 '메모리 주소'를 저장하는 변수입니다. 주소 자체는 정수형으로 간주되어 보통 4바이트(32비트) 크기를 가집니다. 하지만 포인터가 가리키는 데이터 타입에 따라 실제 메모리에서 차지하는 크기와 의미가 달라집니다.
char
타입 변수의 포인터 : 1바이트 크기의 값을 가리킴int
타입 변수의 포인터 : 4바이트 크기의 값을 가리킴float
타입 변수의 포인터 : 4바이트 크기의 값을 가리킴
&a는
변수 a의
주소를 의미하며, int* p
는 정수형 변수의 주소를 저장하는 포인터 변수 p
를 선언한 것입니다.
2. 포인터 선언과 활용 예제
포인터 변수 선언과 주소 할당의 기본 문법은 다음과 같습니다.
int i = 10;
int* p; // 포인터 선언
p = &i; // 변수 i의 주소를 p에 저장
int a = *p; // 포인터 p가 가리키는 주소의 값을 a에 대입
포인터 연산과 관련하여 자주 헷갈리는 표현들을 아래에 정리했습니다.
A = *p++;
:p
가 가리키는 값을A
에 대입 후,p
를 다음 주소로 증가시킴.A = (*p)++;
:p
가 가리키는 값을A
에 대입 후, 그 값 자체를 증가시킴.A = *++p;
:p
의 주소를 먼저 증가시키고, 그 주소가 가리키는 값을A
에 대입.A = ++*p;
:p
가 가리키는 값을 먼저 증가시키고, 그 값을A
에 대입.
3. 포인터와 형변환
포인터도 변수이므로 자료형이 다르면 호환되지 않습니다. 만약 포인터 간 형변환이 필요하다면 명시적으로 캐스팅해야 합니다.
double f = 3.14;
double* pd = &f;
int* pi;
pi = (int*)pd; // double 포인터를 int 포인터로 형변환
4. 함수 호출 시 인수 전달 방식과 포인터
C언어는 기본적으로 값에 의한 호출(Call by Value) 방식을 사용합니다. 함수에 인수를 전달할 때 복사본이 전달되므로 함수 내부에서 변수 값을 변경해도 원본은 변하지 않습니다.
하지만 포인터를 인수로 전달하면 참조에 의한 호출(Call by Reference) 효과를 낼 수 있습니다. 포인터가 실제 데이터의 주소를 전달하므로, 함수 내부에서 포인터를 통해 값을 변경하면 원본 데이터도 바뀌게 됩니다.
예를 들어 scanf()
함수는 변수의 주소를 전달받아 값을 입력받는 방식입니다.
5. 포인터 사용 시 주의할 점
- 포인터를 선언만 하고 초기화하지 않으면 임의의 주소를 가리키게 되어, 프로그램이 비정상 동작하거나 크래시가 발생할 수 있습니다. 따라서
int* p = NULL;
같이 초기화하는 습관이 중요합니다. - 포인터의 자료형과 가리키는 변수의 자료형이 반드시 일치해야 합니다. 일치하지 않으면 메모리 접근 오류가 발생할 수 있습니다.
조금만 실수해도 심각한 버그나 보안 취약점으로 이어질 수 있으니 신중하게 다루어야 합니다.
6. 배열과 포인터의 관계
배열 이름은 사실 배열의 첫 번째 원소의 주소를 나타내는 포인터와 같습니다.
a [0]는
*a와
같다.a [1]
은*(a+1)과
같다.
7. 이중 포인터
이중 포인터는 '포인터를 가리키는 포인터'입니다. 선언은 int** q;
와 같이 하며, 두 단계의 주소 역참조가 가능합니다.
쉽게 말해, '두 번 움직이는 포인터'라고 이해할 수 있습니다.
8. 포인터 배열과 함수 포인터
포인터 배열은 포인터 여러 개를 모아놓은 배열입니다.
int* ap[10]; // int형 포인터 10개가 모인 배열
함수 포인터는 함수를 가리키는 포인터입니다.
int add(int a, int b) {
return a + b;
}
int (*pf)(int, int); // 함수 포인터 선언
pf = add; // add 함수 주소 대입
int result = pf(10, 20); // 함수 포인터로 함수 호출
함수 포인터 배열도 가능하며, 여러 함수 주소를 배열로 관리할 수 있습니다.
int (*pf[4])(int, int) = {add, sub, mul, div};
9. const, volatile, void 포인터
const char* p
: 포인터가 가리키는 데이터 내용은 변경 불가char* const p
: 포인터 자체가 변경 불가(주소 고정)volatile char* p
: 하드웨어 등으로 인해 데이터가 수시로 변경될 수 있으니 매번 메모리에서 다시 읽음void* p
: 자료형이 정해지지 않은 포인터로, 어떤 타입의 주소든 저장 가능
10. 포인터를 배워야 하는 이유
포인터는 고급 자료구조(연결 리스트, 이진트리 등)를 구현할 때 반드시 필요하며, 동적 메모리 할당에도 필수적입니다. 또한 하드웨어 메모리 매핑에도 포인터 개념이 사용되어 임베디드 프로그래밍에도 매우 중요합니다.
1. 포인터 선언은 반드시 초기화하며,
NULL
포인터 처리를 생활화하세요.2. 주소 출력 시
%p
포맷을 꼭 사용하세요.3. 함수 포인터 배열 선언 시 괄호를 잘 넣어야 합니다. 예:
int (*pf[5])(int,int);
4. 포인터는 신중하게 다뤄야 하며, 기본 개념과 문법을 꼭 숙지하세요.
'과목공부 > C언어' 카테고리의 다른 글
[C언어] C언어 문자열 완벽 정리: NULL 문자부터 문자열 함수, 입출력까지 (0) | 2025.06.14 |
---|---|
[C언어] C언어 포인터 완전 정복: 함수 포인터와 다양한 포인터 활용법 (0) | 2025.06.14 |
[C언어] C언어 함수(Function)의 이해와 활용 – 체계적인 프로그래밍의 시작 (1) | 2025.06.14 |
[C언어] C언어 배열 완전 정복 – 1차원과 2차원 배열의 이해와 활용 (0) | 2025.06.14 |
[C언어] C언어 반복문 완전 정복 – While문, Do-while문, For문과 제어문 (0) | 2025.06.14 |