C에서 함수 포인터의 typedef 이해
인수가있는 함수에 대한 포인터를 typedefs 가진 다른 사람들의 코드를 읽을 때 항상 약간 혼란스러워했습니다. 나는 C로 작성된 수치 알고리즘을 이해하려고 시도하는 동안 그러한 정의를 극복하는 데 시간이 걸렸다는 것을 기억합니다. 따라서 함수에 대한 포인터 (유용한 것과하지 말아야 할 것)에 대한 유용한 typedef를 작성하는 방법에 대한 팁과 생각을 공유 할 수 있습니까? 감사!
signal()
C 표준 의 기능을 고려하십시오 .
extern void (*signal(int, void(*)(int)))(int);
완벽하게 모호하게 명백합니다-정수와 인수로 정수를 가져 와서 아무것도 반환하지 않는 함수에 대한 포인터 인 두 개의 인수를 취하는 함수입니다. ( signal()
)는 정수를 인수로 사용하는 함수에 대한 포인터를 반환하고 반환합니다. 아무것도.
당신이 쓰는 경우 :
typedef void (*SignalHandler)(int signum);
대신 다음 signal()
과 같이 선언 할 수 있습니다 .
extern SignalHandler signal(int signum, SignalHandler handler);
이것은 같은 것을 의미하지만 일반적으로 읽기가 다소 쉬운 것으로 간주됩니다. 함수가 int
and a를 가져와 a SignalHandler
를 반환하는 것이 더 명확합니다 SignalHandler
.
그러나 익숙해지기까지는 약간의 시간이 걸립니다. 당신이 할 수없는 한 가지는 SignalHandler
typedef
함수 정의에서를 사용하여 신호 처리기 함수를 작성하는 것 입니다.
나는 여전히 다음과 같이 함수 포인터를 호출하는 것을 선호하는 구식 학교입니다.
(*functionpointer)(arg1, arg2, ...);
현대 구문은 다음을 사용합니다.
functionpointer(arg1, arg2, ...);
그 이유가 무엇인지 알 수 있습니다 functionpointer
. 라는 함수가 아니라 변수가 초기화 된 위치를 찾아야한다는 것을 선호합니다 .
샘은 다음과 같이 언급했다.
나는이 설명을 전에 보았다. 그리고 지금처럼, 내가 얻지 못한 것은 두 진술 사이의 연결이라고 생각합니다.
extern void (*signal(int, void()(int)))(int); /*and*/ typedef void (*SignalHandler)(int signum); extern SignalHandler signal(int signum, SignalHandler handler);
또는 내가 묻고 싶은 것은 두 번째 버전을 생각해 내기 위해 사용할 수있는 기본 개념은 무엇입니까? "SignalHandler"와 첫 번째 typedef를 연결하는 기본은 무엇입니까? 여기서 설명해야 할 것은 typedef가 실제로 여기에서 수행하는 것입니다.
다시 해보자. 이 중 첫 번째는 C 표준에서 직접 해제되어 다시 입력하고 괄호가 올바른지 확인했습니다 (수정하기 전까지는 기억하기 힘든 쿠키입니다).
우선 typedef
, 타입에 대한 별칭을 소개 한다는 것을 기억하십시오 . 따라서 별칭은 SignalHandler
입니다. 유형은 다음과 같습니다.
정수를 인수로 사용하고 아무것도 리턴하지 않는 함수에 대한 포인터
'아무것도 돌려주지 않는다'부분은 철자이다 void
; 정수인 인수는 (설명) 자명하다. 다음 표기법은 단순히 C가 지정된대로 인수를 가져와 주어진 유형을 반환하는 함수에 대한 포인터를 철자하는 방법입니다.
type (*function)(argtypes);
신호 처리기 유형을 만든 후에는이를 사용하여 변수 등을 선언 할 수 있습니다. 예를 들면 다음과 같습니다.
static void alarm_catcher(int signum)
{
fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}
static void signal_catcher(int signum)
{
fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
exit(1);
}
static struct Handlers
{
int signum;
SignalHandler handler;
} handler[] =
{
{ SIGALRM, alarm_catcher },
{ SIGINT, signal_catcher },
{ SIGQUIT, signal_catcher },
};
int main(void)
{
size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
size_t i;
for (i = 0; i < num_handlers; i++)
{
SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
if (old_handler != SIG_IGN)
old_handler = signal(handler[i].signum, handler[i].handler);
assert(old_handler == SIG_IGN);
}
...continue with ordinary processing...
return(EXIT_SUCCESS);
}
신호 처리기에서 사용하지 않는 방법에 유의하십시오 .printf()
그렇다면 코드를 깔끔하게 컴파일하는 데 필요한 표준 헤더 4 개를 제외하고 여기서 무엇을 했습니까?
처음 두 함수는 단일 정수를 취하고 아무것도 반환하지 않는 함수입니다. 그들 중 하나는 실제로 덕분에 전혀 반환하지 않지만 exit(1);
다른 하나는 메시지를 인쇄 한 후에 반환됩니다. C 표준을 사용하면 신호 처리기 내부에서 많은 작업을 수행 할 수 없습니다. POSIX 는 허용되는 것보다 조금 관대하지만 공식적으로 전화를 제재하지는 않습니다 fprintf()
. 수신 된 신호 번호도 인쇄합니다. 에서 alarm_handler()
함수 값은 항상있을 것입니다 SIGALRM
그게에 대한 핸들러이지만,있는 유일한 신호로 signal_handler()
얻을 수 있습니다 SIGINT
또는 SIGQUIT
동일한 기능이 모두 사용되기 때문에 신호 수있다.
그런 다음 각 요소가 신호 번호와 해당 신호에 설치할 핸들러를 식별하는 구조 배열을 만듭니다. 나는 3 가지 신호에 대해 걱정하기로했다. 나는 종종 SIGHUP
, SIGPIPE
그리고 SIGTERM
또한 그것들이 정의되어 있는지 ( #ifdef
조건부 컴파일) 에 대해 걱정 하지만, 그것은 단지 상황을 복잡하게 만듭니다. 나는 아마도 POSIX sigaction()
대신 POSIX 를 사용 signal()
하지만 다른 문제입니다. 우리가 시작한 것을 고수합시다.
이 main()
함수는 설치할 핸들러 목록을 반복합니다. 각 핸들러에 대해 먼저 signal()
프로세스가 현재 신호를 무시하고 있는지 확인하기 위해 호출 하고, 그렇게하는 동안 SIG_IGN
신호가 무시되도록 핸들러로 설치합니다 . 신호가 이전에 무시되지 않았 으면 signal()
이번에는 선호하는 신호 처리기를 설치하기 위해 다시 호출 합니다. (다른 값은 아마 인 SIG_DFL
신호의 기본 신호 처리기,.)에 대한 첫 번째 호출 '신호가 ()'에 핸들러를 설정하기 때문에 SIG_IGN
하고 signal()
이전 오류 처리기의 값 반환 old
애프터 if
문이 있어야합니다 SIG_IGN
- 따라서 주장을. (음, 그것은 할 수 있었다SIG_ERR
무언가가 극도로 잘못되면-어썰트 발사에서 그것에 대해 배울 것입니다.)
그런 다음 프로그램이 작업을 수행하고 정상적으로 종료됩니다.
함수 이름은 적절한 유형의 함수에 대한 포인터로 간주 될 수 있습니다. 이니셜 라이저와 같이 함수 호출 괄호를 적용하지 않으면 함수 이름이 함수 포인터가됩니다. 이것이 pointertofunction(arg1, arg2)
표기법을 통해 함수를 호출하는 것이 합리적인 이유이기도합니다 . 당신이 볼 때 alarm_handler(1)
, 당신은 고려할 수있는 alarm_handler
기능에 대한 포인터이며, 따라서 alarm_handler(1)
함수 포인터를 통해 함수의 호출이다.
따라서 지금까지 SignalHandler
변수에 할당 할 올바른 유형의 값 이있는 한 변수가 비교적 간단하다는 것을 알았습니다. 이것은 두 신호 처리기 함수가 제공하는 것입니다.
이제 우리는 다시 두 가지 선언이 signal()
서로 어떻게 관련 되는지에 대한 질문으로 돌아갑니다 .
두 번째 선언을 검토하겠습니다.
extern SignalHandler signal(int signum, SignalHandler handler);
함수 이름과 유형을 다음과 같이 변경 한 경우 :
extern double function(int num1, double num2);
당신은 아무 문제받는 함수로이 해석이없는 것 int
하고를 double
인수로 리턴 double
?하지만 어쩌면 당신은 하드와 같은 질문에 대해 신중해야 - (값을 당신을 것이라고이 문제가 있다면 아마 당신은 더 나은 '자백하지 않는 게 좋을을 이것이 문제라면 이것으로).
이제 대신 인의 double
상기 signal()
함수는 소요 SignalHandler
번째 인수로하고, 그 결과로 하나의 반환한다.
이를 처리 할 수있는 메커니즘 :
extern void (*signal(int signum, void(*handler)(int signum)))(int signum);
설명하기 까다로워서 아마 망칠 것입니다. 이번에는 매개 변수 이름을 지정했지만 이름은 중요하지 않습니다.
일반적으로 C에서 선언 메커니즘은 다음과 같이 작성하면됩니다.
type var;
당신이 쓸 때 var
그것은 주어진 값을 나타냅니다 type
. 예를 들면 다음과 같습니다.
int i; // i is an int
int *ip; // *ip is an int, so ip is a pointer to an integer
int abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
// function returning an int and taking an int argument
표준에서는 typedef
문법에서 스토리지 클래스로 취급되며 스토리지 클래스와 유사 static
하며 extern
스토리지 클래스입니다.
typedef void (*SignalHandler)(int signum);
즉, 유형의 변수 SignalHandler
(예 : alarm_handler)가 다음과 같이 호출되면
(*alarm_handler)(-1);
결과는 type void
- 결과 가 없습니다. 그리고 with 인수 (*alarm_handler)(-1);
의 호출입니다 .alarm_handler()
-1
우리가 선언하면 :
extern SignalHandler alt_signal(void);
그것은 다음을 의미합니다.
(*alt_signal)();
void 값을 나타냅니다. 따라서:
extern void (*alt_signal(void))(int signum);
동일합니다. 이제는 signal()
a를 반환 할뿐만 아니라 SignalHandler
int와 a SignalHandler
를 모두 인수로 받아들이 기 때문에 더 복잡합니다 .
extern void (*signal(int signum, SignalHandler handler))(int signum);
extern void (*signal(int signum, void (*handler)(int signum)))(int signum);
그래도 여전히 혼란 스러울 경우, 어떻게 도와야할지 모르겠습니다. 여전히 몇 가지 수준에 미스터리하지만, 작동 방식에 익숙해 져서 25 년 동안 더 고집한다면 그래서, 그것은 당신에게 두 번째 본성이 될 것입니다 (그리고 영리하다면 조금 더 빠를 수도 있습니다).
함수 포인터는 다른 포인터와 비슷하지만 데이터 주소 (힙 또는 스택) 대신 함수 주소를 가리 킵니다. 다른 포인터와 마찬가지로 올바르게 입력해야합니다. 함수는 반환 값과 허용되는 매개 변수 유형으로 정의됩니다. 따라서 함수를 완전히 설명하려면 리턴 값을 포함시켜야하며 각 매개 변수의 유형이 승인됩니다. 이러한 정의를 typedef 할 때 해당 정의를 사용하여 포인터를보다 쉽게 작성하고 참조 할 수있는 '친숙한 이름'을 지정하십시오.
예를 들어 함수가 있다고 가정하십시오.
float doMultiplication (float num1, float num2 ) {
return num1 * num2; }
다음 typedef :
typedef float(*pt2Func)(float, float);
이 doMulitplication
기능 을 가리키는 데 사용할 수 있습니다 . 단순히 float를 반환하고 각각 float 유형의 두 매개 변수를 사용하는 함수에 대한 포인터를 정의하는 것입니다. 이 정의는 이름이 친숙 pt2Func
합니다. 참고 pt2Func
float를 반환하고이 수레에 걸리는 모든 기능을 가리킬 수 있습니다.
따라서 다음과 같이 doMultiplication 함수를 가리키는 포인터를 만들 수 있습니다.
pt2Func *myFnPtr = &doMultiplication;
다음과 같이이 포인터를 사용하여 함수를 호출 할 수 있습니다.
float result = (*myFnPtr)(2.0, 5.1);
이것은 좋은 독서를합니다 : http://www.newty.de/fpt/index.html
함수 포인터의 typedef를 이해하는 매우 쉬운 방법 :
int add(int a, int b)
{
return (a+b);
}
typedef int (*add_integer)(int, int); //declaration of function pointer
int main()
{
add_integer addition = add; //typedef assigns a new variable i.e. "addition" to original function "add"
int c = addition(11, 11); //calling function via new variable
printf("%d",c);
return 0;
}
cdecl
함수 포인터 선언과 같은 이상한 구문을 해독하는 데 유용한 도구입니다. 이를 사용하여 생성 할 수도 있습니다.
복잡한 선언을 향후 유지 관리를 위해 쉽게 해석 할 수있는 팁 (자신 또는 다른 사람이)을 결정하는 한, typedef
작은 청크를 만들고 그 작은 조각을 더 크고 복잡한 표현을위한 빌딩 블록으로 사용하는 것이 좋습니다 . 예를 들면 다음과 같습니다.
typedef int (*FUNC_TYPE_1)(void);
typedef double (*FUNC_TYPE_2)(void);
typedef FUNC_TYPE_1 (*FUNC_TYPE_3)(FUNC_TYPE_2);
오히려
typedef int (*(*FUNC_TYPE_3)(double (*)(void)))(void);
cdecl
이 물건으로 당신을 도울 수 있습니다 :
cdecl> explain int (*FUNC_TYPE_1)(void)
declare FUNC_TYPE_1 as pointer to function (void) returning int
cdecl> explain double (*FUNC_TYPE_2)(void)
declare FUNC_TYPE_2 as pointer to function (void) returning double
cdecl> declare FUNC_TYPE_3 as pointer to function (pointer to function (void) returning double) returning pointer to function (void) returning int
int (*(*FUNC_TYPE_3)(double (*)(void )))(void )
그리고 (실제로) 위의 미친 혼란을 어떻게 정확하게 생성했는지입니다.
int add(int a, int b)
{
return (a+b);
}
int minus(int a, int b)
{
return (a-b);
}
typedef int (*math_func)(int, int); //declaration of function pointer
int main()
{
math_func addition = add; //typedef assigns a new variable i.e. "addition" to original function "add"
math_func substract = minus; //typedef assigns a new variable i.e. "substract" to original function "minus"
int c = addition(11, 11); //calling function via new variable
printf("%d\n",c);
c = substract(11, 5); //calling function via new variable
printf("%d",c);
return 0;
}
이것의 출력은 다음과 같습니다.
22
6
함수를 선언하는 데 동일한 math_func 정의자가 사용되었습니다.
extern struct에 typedef와 동일한 접근 방식을 사용할 수 있습니다 (다른 파일에서 sturuct 사용).
이것은 내가 연습으로 쓴 함수 포인터와 함수 포인터 배열의 가장 간단한 예입니다.
typedef double (*pf)(double x); /*this defines a type pf */
double f1(double x) { return(x+x);}
double f2(double x) { return(x*x);}
pf pa[] = {f1, f2};
main()
{
pf p;
p = pa[0];
printf("%f\n", p(3.0));
p = pa[1];
printf("%f\n", p(3.0));
}
더 복잡한 유형, 즉 함수 포인터를 정의하려면 typedef를 사용하십시오.
C에서 상태 머신을 정의하는 예를 들어 보겠습니다.
typedef int (*action_handler_t)(void *ctx, void *data);
이제 우리는 두 개의 포인터를 취하고 int를 반환하는 action_handler라는 유형을 정의했습니다.
상태 머신을 정의하십시오
typedef struct
{
state_t curr_state; /* Enum for the Current state */
event_t event; /* Enum for the event */
state_t next_state; /* Enum for the next state */
action_handler_t event_handler; /* Function-pointer to the action */
}state_element;
액션에 대한 함수 포인터는 단순한 타입처럼 보이고 typedef는 주로이 목적을 수행합니다.
이제 모든 이벤트 핸들러가 action_handler에 의해 정의 된 유형을 준수해야합니다.
int handle_event_a(void *fsm_ctx, void *in_msg );
int handle_event_b(void *fsm_ctx, void *in_msg );
참고 문헌 :
Linden의 전문가 C 프로그래밍
참고 URL : https://stackoverflow.com/questions/1591361/understanding-typedefs-for-function-pointers-in-c
'IT story' 카테고리의 다른 글
IIS 7 AppPool ID를 SQL Server 로그온으로 추가 (0) | 2020.04.24 |
---|---|
Java에서 바이트 리터럴을 어떻게 지정합니까? (0) | 2020.04.23 |
안드로이드 앱에서 인터넷 연결을 확인하기위한 방송 수신기 (0) | 2020.04.23 |
JavaScript % (모듈로)는 음수에 대해 음수 결과를 제공합니다. (0) | 2020.04.23 |
Assert.Throws를 사용하여 예외 유형을 지정하려면 어떻게합니까? (0) | 2020.04.23 |