라즈베리파이를 c언어로 다루기 위해, 이것저것 공부를 하고 있다.
그러던 와중, 함수포인터가 매우 자주 보여 이를 정리하고자 한다!
원리 위주로 파해칠 예정이며, 활용은 다른 게시글 참고바란다!
그리고 제일 중요한 것은, 포인터를 잘 모르겠으면 무조건 이해하고 와야한다.
꼭!!!! 무조건이다!!
https://dojang.io/mod/page/view.php?id=591
C 언어 코딩 도장: 68.0 함수 포인터 사용하기
C 언어에서 함수는 이름이 정해져 있죠. 그래서 함수를 호출하려면 함수 이름으로 직접 호출했습니다. void hello() { printf("Hello, world!\n"); } int main() { hello(); // 함수 이름으로 함수를 직접 호출 return
dojang.io
이 글을 참조하여 작성했으니, 만약 부족한 설명이 있다면 위 게시글로 이동해서 보길 권장합니다.
void hello()
{
printf("Hello, world!\n");
}
int main()
{
printf("%p\n", hello); // 00D1137F(32비트): 함수 이름도 포인터이므로 메모리 주소가 출력됨
return 0;
}
이런식으로, 포인터를 프린트 하면 주석과 같은 메모리 값이 나올 것으로 예측된다.
반환값도, 매개변수도 없는 함수 포인터 (기본형)
#include <stdio.h>
void hello() // 반환값과 매개변수가 없음
{
printf("Hello, world!\n");
}
void bonjour() // 반환값과 매개변수가 없음
{
printf("bonjour le monde!\n");
}
int main()
{
void (*fp)(); // 반환값과 매개변수가 없는 함수 포인터 fp 선언
fp = hello; // hello 함수의 메모리 주소를 함수 포인터 fp에 저장
fp(); // Hello, world!: 함수 포인터로 hello 함수 호출
fp = bonjour; // bonjour 함수의 메모리 주소를 함수 포인터 fp에 저장
fp(); // bonjour le monde!: 함수 포인터로 bonjour 함수 호출
return 0;
}
이런 식으로, 함수를 정의하고 함수 포인터를 정의하여, 매칭시켜 실행시킬 수 있다.
어찌보면, 변수를 매칭시키는 느낌과 비슷하다!
만약 void (*fp)()라는 것에 어색함을 느낀다면... 포인터를 공부하고 오는 것을 권장한다.
이는 제일 단순한 형태로, 함수 포인터를 정의함으로서
이 함수(fp)가 어느 함수의 메모리 주소를 포인팅, 즉 지시하고 있을지 모르는 상태이다.
그러다가 fp = hello; 로, 함수 포인터가 hello 라는 함수를 지시하게 변경해주는 것.
반환값이 있는 함수 포인터
만약 지시하고 싶은 함수의 모양이 다음과 같은 형태라면 어떻게 될까?
int Get_Age()
{
age = 21;
return age;
}
이런 형태로 되어있다면, 함수 포인터는?
int (*fp)();
이렇게 정의해주면, 그대로 받아 올 수 있다.
fp = Get_Age;
int age = fp();
즉, 형식을 맞게 맞춰주면 되는 것!
입력값이 있는 함수 포인터
void Print_Age(int age)
{
printf("Age is %d\n", age);
}
이런 식으로 입력 값이 있는 함수의 경우 다음과 같은 함수 포인터를 정의해주면 된다.
void (*fn)(int);
이후 매칭 시켜주고, 다음과 같이 실행이 가능하다.
fp = Print_Age;
fp(21);
입력값과 반환값이 있는 함수 포인터
이젠 예상이 되는 것 같다.
int add(int a, int b) // int형 반환값, int형 매개변수 두 개
{
return a + b;
}
int (*fp)(int, int);
이렇게 한 뒤,
fp = add; // add 함수의 메모리 주소를 함수 포인터 fp에 저장
printf("%d\n", fp(10, 20)); // 30: 함수 포인터로 add 함수를 호출하여 합을 구함
이런 식으로 구현 가능!
함수 포인터를 배열로 사용
void (*fp[4])();
이렇게, 일반적인 int a[4] 처럼 이름 뒤에 정의하면 된다.
만약 뒤에 여러 함수를 붙이고싶다면, 일반적인 배열 정의처럼 형식에 맞는 함수로 맞추어 중괄호로 묶어서 정의해주면 된다.
즉, 다음과 같다.
void A(){...}
void B(){...}
void C(){...}
void D(){...}
void (*fp[4])() = {A, B, C, B}
함수의 형식이 복잡해지더라도, 단순하게 생각해야한다!!
함수 포인터를 함수의 매개변수로 사용
이게 무슨 소리인가.. 싶을텐데!
우선 이걸 보자.
void Print_Age(int age)
{
printf("Age is %d\n", age);
}
int 라는 매개변수를 넘겨서 출력하는 것이다.
그러면, 만약 함수 포인터를 넘기게 된다면?
int Get_Age(int age) { return age; }
void Print_Age(int (*fp)(int), int age) {
printf("Age is %d\n", fp(age));
}
...
//대충 여기서부터 메인
int age = 21;
int (*fp)(int) = Get_Age; // 함수 포인터 선언과 함께 초기화
Print_Age(fp, age);
이런 식으로 정리가 가능할 것이다!
함수 포인터를 함수의 반환값으로 사용
int add(int a, int b) // int형 반환값, int형 매개변수 두 개
{
return a + b;
}
이러한 함수를 반환하는 함수를 제작하는 것과 비슷하다!
우선 간단한 버전을 보겠다.
void Print_Age(void)
{ printf("Age is 21\n"); }
void (*fp(void))(void)
{ return Print_Age; }
...
// 함수 포인터를 사용하여 Print_Age 호출
void (*func_ptr)(void) = fp();
func_ptr();
이런 형식으로, *fp 자리에 매개변수 괄호를 더 넣는 식으로 정의된다.
다음은 좀 더 심화 버전이다.
int add(int a, int b) { return a + b; }
int (*getAdd())(int, int) { return add; }
...
int (*fp)(int, int) = getAdd();
printf("%d\n", fp(10, 20)); // 30: getAdd를 호출한 뒤 반환값으로 add 함수 호출
이런식으로, 함수를 받는 함수를 정의할 수 있다.
더 더 꼬아버리면??
int add(int a, int b) { return a + b; }
int (*getAdd(int x, int y))(int, int){
printf("%d %d\n", x, y);
return add;
}
...
int (*fp)(int, int) = getAdd(8, 9);
printf("%d\n", fp(10, 20));
복잡해보이지만, 사실 참고해온 게시글의 코드가 더더 복잡했다. 뇌를 꼬아버리는 수준.
일부러 한 번 풀어 적어두었다.
Typedef로 함수 포인터 정의하고 활용하기
이제 슬슬 뇌가 꼬인다!
int add(int a, int b){ return a + b; }
typedef int (*FP)(int, int); // FP를 함수 포인터 별칭으로 정의
FP getAdd()
{
return add;
}
이렇게 적으면 어떻게 될까?
이에 대한 설명은 다음과 같다.
typedef int (*FP)(int, int); 에서
typedef : 다음으로 정의한다
int (int, int) : 이러한 함수 포인터의 형식으로
FP : FP라는 이름으로!
이해가 잘 안 된다면, struct를 typedef하는 것과 비슷하게 생각해보면 된다.
typedef struct AAA{
....
};
AAA test1;
typedef : 다음으로 정의한다
struct {...} : 이와 같은 구조체의 형식으로
AAA : AAA라는 이름으로!
그래서, 만약 위 함수를 풀어서 쓴다면
typedef int (*FP)(int, int); // FP를 함수 포인터 별칭으로 정의
FP getAdd()
{
return add;
}
/*---------------------------------------------------------------*/
int (*getAdd())(int, int) { return add; }
이렇게, 위와 아래가 같은 것!
사실 참고한 사이트에서도 권장하는 것은, 사용하기 전에 검색해보는 것이다.
점점 복잡해지는 형태이다보니, 헷갈릴 수 있기 때문..
그러니, 한 번 코드를 짜고, gpt 또는 옆에있는 다른 C언어 개발자에게 물어보는 것을 권장한다.
'CS 지식 정리' 카테고리의 다른 글
| AUTOSAR - SW 컴포넌트 개념 (0) | 2025.03.10 |
|---|---|
| 스택이란? - C언어 알아보기 (1) | 2025.03.05 |
| 구조체란? - C언어 알아보기 (2) | 2025.02.25 |
| MCU 스케줄링(Scheduling)의 이해 및 TC275 기반 실습 (0) | 2025.02.18 |
| AUTOSAR 기반 프로그램Folder Tree 분석 (0) | 2025.02.16 |