C语言中传递函数与函数指针的差异及规范用法问询
嘿,我来帮你把这些函数指针的疑惑理清楚——这确实是C语言里一个容易让人懵的点,尤其是因为标准里有一些「隐式转换」的规则在背后悄悄起作用。咱们一个个来拆解你的问题:
1. 这4个片段是否存在实际差异?为何表现完全相同?
这四种写法在标准C里没有任何实际运行时差异。核心原因是C语言对函数名和函数参数的两套自动转换规则:
- 当函数名(比如
print)出现在表达式中(除了作为sizeof、&运算符的操作数,或是字符串化宏的参数时),会自动被转换为指向该函数的指针; - 在函数参数声明里,
void(f)()这种「函数类型」的写法,会被编译器自动调整为void(*f)()——也就是函数指针类型,类似数组参数会退化成指针的规则。
所以不管你传print还是&print,不管参数声明成函数还是函数指针,最终传递的都是同一个函数指针,运行时表现自然完全一致。
2. 若无运行时差异,编译器做了哪些处理?哪种是标准写法?
编译器主要做了两类隐式类型转换:
- 传参阶段:把函数名
print自动转换成&print(函数指针); - 参数声明阶段:把
void(f)()这种函数类型的参数,调整为void(*f)()函数指针类型——这是C标准明确规定的语法规则。
两种写法都是标准允许的,但**void(*f)()是更清晰的标准写法**,它直接明确了参数是函数指针;而void(f)()属于语法糖,本质和前者等价。传参时print和&print也都符合标准,效果无差。
3. 应选择哪种写法?
推荐你使用**传参用print,参数声明用void(*f)()**的组合:
void(*f)()的声明方式能让其他开发者一眼看懂这是个函数指针参数,避免歧义;- 传参用
print是C社区最常用的写法,简洁且符合大众编码习惯。
如果团队有统一的编码规范,优先遵循规范即可。
4. 调用传入的函数时用f()还是(*f)()?
两种写法完全等价,随便用哪个都可以。因为当你对函数指针用*解引用时,得到的函数类型会立刻被转换回函数指针(除非在sizeof或&操作里)——甚至你写(**f)()、(***f)()也能正常运行,但完全没必要多此一举。f()是最简洁直观的写法,优先用这个。
5. 为何sizeof(print)结果为1?此时获取的是什么大小?
这其实是编译器的非标准扩展行为。因为C标准明确规定:sizeof不能作用于函数类型。你的Apple LLVM编译器允许了这个操作,但返回的1只是一个占位值——函数是代码段里的指令序列,根本没有「大小」这个概念。
如果你加上-pedantic编译选项,编译器会给出警告,提示你sizeof不能用于函数类型,这才是符合标准的反馈。
6. 为何片段1、3中,参数声明为void(f)()时sizeof(f)仍为8?
因为前面提到的参数类型调整规则:在函数参数声明里,void(f)()会被编译器自动转换成void(*f)()——也就是说,f本质上是一个函数指针变量,而不是函数类型。在64位的macOS系统里,指针的大小是8字节,所以sizeof(f)返回8是完全符合标准的结果。
内容的提问来源于stack exchange,提问作者Rafael Eyng

