能否对函数名使用#ifndef?MicroPython编译相关疑问
#ifndef检测函数存在性的问题 首先得掰扯清楚一个核心点:C/C++的预处理器(就是处理#ifndef这类指令的工具)完全不知道函数、变量这些符号的存在——它本质就是个文本替换器,工作阶段远早于编译器的符号解析环节。所以你用#ifndef 函数名这种写法从根上就不对,预处理器只会把它当成“这个标识符有没有被#define过”来判断,和函数实际有没有定义半毛钱关系都没有。
为什么你的测试输出全是5?
举个你可能用到的测试代码例子:
#include <stdio.h> // 有时候注释掉这个函数定义,有时候保留 // int foo() { return 5; } #ifndef foo #define foo() 5 #endif int main() { printf("%d\n", foo()); return 0; }
这里预处理器只认#define出来的foo,不管你有没有写int foo()这个函数。哪怕你定义了函数,只要没手动#define foo,#ifndef foo就会判定为“真”,所以会把foo()替换成5,输出自然是5;如果没定义函数,同样因为没#define foo,还是会替换成5。这真不是编译器bug,就是预处理器的工作机制本来就如此。
那要怎么检测函数是否存在?
如果确实要在代码里判断某个函数是否存在,得用编译器的内置扩展特性,比如GCC或者Clang支持的__builtin_exists(注意这是非标准特性,不同编译器支持情况不一样):
#include <stdio.h> // 试试注释/取消注释这个函数 int foo() { return 5; } int main() { #if __builtin_exists(foo) printf("%d\n", foo()); #else printf("5\n"); #endif return 0; }
要是你写的是跨平台、跨编译器的代码,更稳妥的方式是用编译前的配置工具(比如CMake、Autotools)先检测目标环境里有没有这个函数,然后生成一个带定义宏的头文件(比如config.h),再在代码里用#ifdef HAVE_FOO这类宏来判断,而不是直接碰函数名。
再回到MicroPython的场景
MicroPython里大量用预处理指令,一般都是用来处理平台差异、功能开关(比如#ifdef MICROPY_PY_UJSON这类控制是否编译某模块的宏),从来不会直接用#ifndef 函数名来检测函数存在。如果你看到类似写法,大概率是那个函数对应的功能宏已经在某个地方被#define过了,比如:
#define HAVE_FOO 1 int foo() { ... }
然后用#ifdef HAVE_FOO来判断,和函数本身的存在性绑定起来,而不是直接检测函数名。
总结一下:#ifndef本来就不能用来检测函数是否存在,这是预处理器的设计逻辑决定的,不是编译器bug。要实现这个需求,要么用编译器的内置扩展,要么靠编译前的配置工具生成判断宏。
内容的提问来源于stack exchange,提问作者Bob

