C语言原型签名参数:函数签名与函数指针的差异问询
背景内容
main函数中支持函数的源代码:
static char * string(int i) { char * temp = malloc (sizeof *temp * strlen("1 My function")); temp = strcpy(temp, "0 My function"); if (i) { temp[0]+=i; return temp; } else return NULL; }
签名嵌套的几种形式(含函数指针与函数签名):
static char * signaturesA (int i, char * func(int j)){ return (*(&func))(i); } static char * signaturesB (int i, char (* func)(int j)){ return func(i); } static char * sigptrsA (int i, char * func(int j)){ return func(i); } static char * sigptrsB (int i, char * (*func)(int j)){ return (*func)(i); } int main(void) { puts(signaturesA(1, string)); puts(signaturesB(2, string)); puts(sigptrsA(3, string)); puts(sigptrsB(4, string)); return 0; }
GDB调试终端输出(加粗为函数终端输出):
Breakpoint 1, signaturesA (i=1, func=0x555555555440 ) at keyboard.c:90 90 return (*(&func))(i); 1: func = (char * (*)(int)) 0x555555555440 2: *func = {char *(int)} 0x555555555440 11: &func = (char *(**)(int)) 0x7fffffffddf0 **1 My function** Breakpoint 2, signaturesB (i=2, func=0x555555555440 ) at keyboard.c:93 93 return func(i); 3: func = (char * (*)(int)) 0x555555555440 4: *func = {char *(int)} 0x555555555440 10: &func = (char *(**)(int)) 0x7fffffffddf0 **2 My function** Breakpoint 3, sigptrsA (i=3, func=0x555555555440 ) at keyboard.c:96 96 return func(i); 5: func = (char * (*)(int)) 0x555555555440 6: *func = {char *(int)} 0x555555555440 12: &func = (char *(**)(int)) 0x7fffffffddf0 **3 My function** Breakpoint 4, sigptrsB (i=4, func=0x555555555440 ) at keyboard.c:99 99 return (*func)(i); 7: func = (char * (*)(int)) 0x555555555440 8: *func = {char *(int)} 0x555555555440 9: &func = (char * (**)(int)) 0x7fffffffddf0 **4 My function**
技术问题解答
1. 三种函数寻址表示风格的用途
func = (char * (*)(int)) 0x555555555440:这是GDB对函数指针变量的类型标注,用括号包裹的强制类型转换格式,明确表示func是一个指向“返回char*、参数为int”的函数的指针,对应的地址是函数的内存入口。*func = {char *(int)} 0x555555555440:大括号格式是GDB对函数本身类型的展示。对函数指针解引用*func得到的是函数实体,GDB用{char *(int)}标注这个实体是“返回char*、参数为int”的函数,地址同样是函数的入口地址。&func = (char * (**)(int)) 0x7fffffffddf0:这是对函数指针变量自身地址的类型标注,(char * (**)(int))表示这是一个指向“指向返回char*、参数为int的函数的指针”的指针(即函数指针的指针),对应的地址是func变量在栈上的存储位置。
2. 两种函数参数风格的用途
static char * sigptrsA (int i, char * func (int j));:这是C语言的语法糖——当把函数签名char * func(int j)作为参数时,编译器会自动将其转换为对应的函数指针类型char * (*func)(int j)。这种写法更直观,贴近普通函数调用的语义,适合追求代码简洁、可读性的场景。static char * sigptrsB (int i, char * (*func)(int j));:这是显式声明的函数指针参数,完全明确地告知编译器和代码阅读者,func是一个指向特定类型函数的指针。无语法糖的写法类型声明更清晰,适合复杂回调、函数表定义等需要强调“这是指针”的场景,能避免歧义。
3. 额外需要考虑的内容
- 隐式转换规则:C语言中函数名可自动转换为函数指针,但反向转换不允许;不同类型的函数指针之间转换属于未定义行为,必须显式强制转换,否则可能引发调用错误。
- 调用语法兼容性:
func(i)、(*func)(i)甚至(**func)(i)在C中都是合法写法——函数指针解引用后使用时,会再次自动转换为函数指针,几种写法效果完全一致,仅风格不同。 - 存储与生命周期:函数指针变量可存储在栈、全局区等,而函数本身通常存放在代码段(只读区),注意不要修改函数指针指向的函数内容,否则会触发段错误。
- 架构兼容性:部分嵌入式架构中,函数指针的大小可能与普通指针不同,使用时需注意类型匹配,避免内存访问错误。
- typedef简化声明:复杂函数指针类型可用
typedef简化,比如typedef char * (*StrFunc)(int);,后续可直接用StrFunc代替冗长的类型声明,提升代码可读性。
内容的提问来源于Stack Exchange,提问作者Carl Johnson
相关产品推荐
相关产品推荐

