고급 포인터
포인터는 C 언어에서 함수에 인수를 전달할 때 매우 중요한 역할을 합니다. 일반적으로 함수에 인수를 전달하면 값이 복사되어 함수 내부에서만 사용되고, 함수가 종료되면 복사된 값은 사라집니다. 하지만 포인터를 통해 변수의 주소를 전달하면, 함수 내부에서 직접 원본 변수의 값을 변경할 수 있습니다. 이렇게 하면 함수가 외부 변수에 직접 영향을 주므로, 복수의 값을 반환하거나 큰 데이터를 복사하지 않고 처리할 때 효율적입니다.
예를 들어, 함수에서 변수의 값을 수정해야 하거나, 배열 같은 복잡한 데이터 구조를 전달할 때는 포인터를 사용하여 메모리 주소를 넘겨줌으로써 원본 데이터를 조작할 수 있습니다. 이 방식은 특히 메모리 사용 최적화와 프로그램의 성능 향상에 매우 중요합니다.
예시 코드: 포인터를 이용한 값 변경
#include <stdio.h>
void updateValue(int *p) {
*p = 100; // 포인터가 가리키는 주소의 값을 100으로 변경
}
int main() {
int x = 10;
printf("변경 전 값: %d\n", x); // 출력: 10
updateValue(&x); // 변수 x의 주소를 전달
printf("변경 후 값: %d\n", x); // 출력: 100
return 0;
}
위 코드에서 updateValue 함수는 정수형 포인터를 매개변수로 받습니다. main 함수에서 변수 x의 주소를 넘겨주면, updateValue 함수 내부에서 포인터 역참조를 통해 직접 변수 x의 값을 변경합니다. 이를 통해 값 전달과 달리 함수 호출 후에도 변수 x의 값이 변경된 상태로 유지됩니다.
포인터와 문자열
문자열은 문자 배열의 특별한 형태이며, C 언어에서 문자열을 다룰 때 포인터는 핵심적인 역할을 합니다. 문자열의 시작 주소를 가리키는 포인터를 통해 문자열 전체를 관리할 수 있고, 문자 단위로 접근하거나 조작할 수 있습니다. 문자열은 보통 null 문자(\0)로 끝나기 때문에 포인터를 이용해 문자를 하나씩 탐색하는 것이 일반적입니다.
또한, 문자열 리터럴은 읽기 전용 메모리에 저장되기 때문에, 이를 가리키는 포인터로 문자열을 출력하거나 읽는 것은 가능하지만 직접 수정하는 것은 안전하지 않습니다. 문자열을 수정하려면 문자 배열을 선언하거나 동적 메모리 할당을 이용해야 합니다.
예시 코드: 문자열 포인터
#include <stdio.h>
int main() {
char *str = "Hello, World!"; // 문자열 리터럴을 가리키는 포인터
printf("%s\n", str); // 출력: Hello, World!
// 포인터 산술을 이용해 문자열의 각 문자 출력
for (int i = 0; str[i] != '\0'; i++) {
printf("문자 %d: %c\n", i, *(str + i));
}
return 0;
}
위 코드에서 str 포인터는 문자열 리터럴의 시작 주소를 가리키고 있습니다. 포인터 산술(*(str + i))을 통해 문자열의 각 문자에 접근할 수 있습니다. 이처럼 포인터는 문자열 처리를 간결하고 효율적으로 할 수 있게 도와줍니다.
void 포인터
void* 포인터는 어떤 자료형이든 가리킬 수 있는 제너럴 포인터입니다. 즉, 특정 타입이 정해지지 않은 포인터로, 다양한 데이터 타입의 주소를 하나의 포인터 타입으로 처리할 때 유용합니다. 하지만 void*는 역참조가 불가능하며, 반드시 원래 타입으로 형변환(casting) 한 후에 접근해야 합니다.
void*는 주로 라이브러리 함수, 메모리 할당 함수(malloc 등)에서 사용되며, 제네릭 프로그래밍 기법에 필수적인 역할을 합니다. 다만, 형변환 시 원본 데이터 타입과 맞지 않으면 심각한 오류가 발생할 수 있으므로 주의해야 합니다.
예시 코드: void 포인터 사용
#include <stdio.h>
int main() {
int num = 42;
void *ptr = # // int형 변수 주소를 void 포인터에 저장
// void 포인터는 역참조 불가, 반드시 형변환 후 사용
printf("값: %d\n", *(int *)ptr);
double dnum = 3.14;
ptr = &dnum; // double형 변수 주소 저장
printf("값: %f\n", *(double *)ptr);
return 0;
}
void* 포인터는 모든 자료형을 담을 수 있어 매우 유연하지만, 타입 안정성을 보장하지 않기 때문에 실제 사용 시 올바른 타입으로 변환하는 것이 필수입니다. 잘못된 형변환은 프로그램 오류 및 예기치 않은 동작을 유발할 수 있으므로 주의해야 합니다.
함수 포인터
함수 포인터는 함수의 주소를 저장할 수 있는 포인터로, 이를 통해 함수를 간접적으로 호출할 수 있습니다. 콜백 함수 구현, 이벤트 핸들링, 동적 함수 호출 등에서 매우 유용하며, 함수 호출의 유연성을 높여줍니다. 함수 포인터는 함수의 반환형과 매개변수 타입을 명확히 지정해 선언하며, 이 선언 형식이 맞지 않으면 컴파일 에러가 발생합니다.
함수 포인터를 활용하면 여러 함수를 배열 형태로 저장하고 필요에 따라 호출할 수 있으며, 프로그램의 확장성과 유지보수성을 크게 향상합니다.
예시 코드: 함수 포인터 사용
#include <stdio.h>
void greet() {
printf("안녕하세요!\n");
}
void farewell() {
printf("안녕히 가세요!\n");
}
int main() {
// 함수 포인터 선언: 반환형 void, 매개변수 없음
void (*funcPtr)();
funcPtr = greet; // greet 함수 주소 저장
funcPtr(); // greet 함수 호출
funcPtr = farewell; // farewell 함수 주소 저장
funcPtr(); // farewell 함수 호출
// 함수 포인터 배열 사용 예제
void (*funcArr[])() = {greet, farewell};
for (int i = 0; i < 2; i++) {
funcArr[i]();
}
return 0;
}
위 예제는 함수 포인터를 선언하고, 이를 통해 서로 다른 함수를 간접 호출하는 모습을 보여줍니다. 또한 함수 포인터 배열을 활용하여 여러 함수를 관리하고 반복적으로 호출하는 방법도 함께 소개합니다. 함수 포인터를 잘 활용하면 콜백 패턴 구현이 쉬워지고, 코드의 유연성과 재사용성이 크게 증가합니다.
요약
포인터는 C 언어의 핵심 개념으로, 함수 인자 전달 시 값이 아닌 주소를 전달하여 함수 내부에서 직접 변수 값을 조작할 수 있게 해 줍니다. 문자열 처리에서도 포인터는 문자열의 시작 주소를 가리켜 효율적인 문자열 조작을 가능하게 합니다. void* 포인터는 모든 자료형을 가리킬 수 있는 범용 포인터로, 제네릭 프로그래밍에 필수적이지만 역참조 시 반드시 형변환이 필요합니다. 마지막으로 함수 포인터는 함수의 주소를 저장하고 간접 호출을 가능하게 해 주어 콜백 함수 구현과 동적 함수 호출에 매우 유용합니다. 이러한 고급 포인터 사용법을 숙지하면 C 언어 프로그래밍 능력이 크게 향상됩니다.
'Programming' 카테고리의 다른 글
| C 함수 (63) | 2025.08.14 |
|---|---|
| C 다차원 배열과 포인터 (62) | 2025.08.13 |
| C 동적 메모리 할당 (80) | 2025.08.11 |
| C 제어문 (67) | 2025.08.10 |
| C 연산자 (74) | 2025.08.09 |