함수 포인터 변수란?
함수 포인터는 특정 함수의 시작 주소를 저장할 수 있는 포인터입니다. 이를 통해 함수를 직접 호출하는 대신, 포인터를 통해 간접적으로 함수를 실행할 수 있습니다. C 언어에서 함수 이름 자체가 그 함수의 주소를 의미하므로, 다음과 같이 출력하여 확인할 수 있습니다.
#include <stdio.h>
void PrintMessage() {
printf("Hello from function!\n");
}
int main() {
printf("함수 주소: %p\n", (void*)PrintMessage);
printf("함수 주소(& 사용): %p\n", (void*)&PrintMessage);
return 0;
}
위 코드에서 두 주소 값은 동일하게 출력됩니다. 이는 함수명이 곧 주소임을 보여줍니다.
함수 포인터의 타입 선언
함수 포인터의 형식은 반환형, 매개변수의 수와 타입을 정확히 지정해야 합니다. 예를 들어, 두 개의 정수를 받아 정수를 반환하는 함수의 포인터는 다음과 같이 선언합니다.
int (*funcPtr)(int, int);
funcPtr: 포인터 변수 이름int: 함수의 반환 타입(int, int): 함수가 받는 인자의 타입
실제로 이를 이용해 덧셈 함수를 호출하는 예제는 아래와 같습니다.
#include <stdio.h>
int Add(int a, int b) {
return a + b;
}
int main() {
int (*operation)(int, int) = Add;
int x = 5, y = 3;
printf("결과: %d\n", operation(x, y)); // 또는 (*operation)(x, y)
return 0;
}
함수 포인터로 호출할 때 괄호 안에 *를 넣는 것은 문법적으로 가능하지만 생략 가능합니다. 컴파일러가 자동으로 해석합니다.
함수 포인터 배열
여러 함수를 하나의 배열에 저장하면, 조건에 따라 적절한 함수를 선택하여 호출할 수 있습니다. 이를 함수 포인터 배열이라 부릅니다. 선언 방식은 다음과 같습니다.
int (*ptrArray[5])(int, int);
배열의 각 요소는 같은 시그니처(반환형과 매개변수)를 가진 함수를 가리켜야 합니다. 인덱스 0은 종종 사용하지 않거나 NULL로 설정합니다.
계산기 프로그램의 세 가지 구현 방식
방법 1: 전통적인 switch-case 기반 구현
각 연산마다 별도의 함수를 만들고, 사용자 입력에 따라 switch 문으로 분기합니다. 단점은 중복된 입력 처리 코드가 반복된다는 점입니다.
#include <stdio.h>
int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }
int Multiply(int a, int b) { return a * b; }
int Divide(int a, int b) { return b != 0 ? a / b : 0; }
void ShowMenu() {
printf("\n=== 계산기 ===\n");
printf("1. 덧셈 2. 뺄셈\n");
printf("3. 곱셈 4. 나눗셈\n");
printf("0. 종료\n");
printf("선택: ");
}
int main() {
int choice, x, y;
do {
ShowMenu();
scanf("%d", &choice);
if (choice == 0) {
printf("프로그램 종료.\n");
break;
}
printf("두 숫자 입력: ");
scanf("%d %d", &x, &y);
switch (choice) {
case 1:
printf("결과: %d\n", Add(x, y));
break;
case 2:
printf("결과: %d\n", Subtract(x, y));
break;
case 3:
printf("결과: %d\n", Multiply(x, y));
break;
case 4:
if (y == 0) {
printf("오류: 0으로 나눌 수 없습니다.\n");
} else {
printf("결과: %d\n", Divide(x, y));
}
break;
default:
printf("잘못된 선택입니다.\n");
}
} while (1);
return 0;
}
방법 2: 함수 포인터 배열 사용
중복된 scanf 및 출력 코드를 제거하고, 함수 포인터 배열을 이용해 연산을 매핑합니다. 유지보수성과 확장성이 향상됩니다.
#include <stdio.h>
int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }
int Multiply(int a, int b) { return a * b; }
int Divide(int a, int b) { return b != 0 ? a / b : 0; }
void ShowMenu() {
printf("\n=== 계산기 (포인터 배열) ===\n");
printf("1. 덧셈 2. 뺄셈 3. 곱셈 4. 나눗셈\n");
printf("0. 종료\n");
printf("선택: ");
}
int main() {
// 함수 포인터 배열 초기화 (인덱스 0은 NULL)
int (*ops[])(int, int) = {NULL, Add, Subtract, Multiply, Divide};
int choice, x, y;
do {
ShowMenu();
scanf("%d", &choice);
if (choice == 0) {
printf("종료합니다.\n");
break;
}
if (choice >= 1 && choice <= 4) {
printf("두 수 입력: ");
scanf("%d %d", &x, &y);
printf("결과: %d\n", ops[choice](x, y));
} else {
printf("잘못된 입력입니다.\n");
}
} while (1);
return 0;
}
방법 3: 함수 포인터를 인자로 전달
공통 로직을 별도의 함수로 추출하고, 연산 함수를 인자로 전달합니다. 코드 재사용성이 극대화됩니다.
#include <stdio.h>
int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }
void ExecuteOperation(int (*operation)(int, int)) {
int x, y;
printf("두 수 입력: ");
scanf("%d %d", &x, &y);
printf("결과: %d\n", operation(x, y));
}
void ShowMenu() {
printf("\n=== 계산기 (함수 포인터 전달) ===\n");
printf("1. 덧셈 2. 뺄셈 0. 종료\n");
printf("선택: ");
}
int main() {
int choice;
do {
ShowMenu();
scanf("%d", &choice);
switch (choice) {
case 1:
ExecuteOperation(Add);
break;
case 2:
ExecuteOperation(Subtract);
break;
case 0:
printf("종료합니다.\n");
break;
default:
printf("잘못된 선택.\n");
}
} while (choice != 0);
return 0;
}