
这里写自定义目录标题问题复现探究原因深究验证总结问题复现最近在做一个项目调试发现同一个枚举值在不同的文件里居然是不一样的数值。这里我先复现一下问题。首先建立两个头文件define.h#ifndefDEFINE_H#defineDEFINE_H#includeenum.h#defineCHOOSE_10#defineCHOOSE_21#defineTESTCHOOSE_1#endifenum.h#ifndefENUM_H#defineENUM_H#includedefine.henum{state_0,#ifTESTCHOOSE_2state_test,#endifstate_what,};voidprint_what();#endif然后我们建立两个c文件a.c#includedefine.h#includeenum.h#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}b.c#includeenum.h#includestdio.hintmain(intargc,char*argv[]){print_what();printf(b:%d\r\n,state_what);}内容很简单定义了一个枚举体在枚举体里有一个宏条件来选择需不需要某个枚举值然后分别在a.c和b.c中显示。我们来运行一下神奇的事情发生了我们TEST CHOOSE_1的情况下枚举体中state_what应该是1才对但是在a.c里面他居然是2,说明state_test在a.c中居然被编译进去了enum{state_0,#ifTESTCHOOSE_2state_test,#endifstate_what,};探究原因我们看看a.c的代码#includedefine.h#includeenum.h#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}超级简单3个头文件一个printf打印那么问题就只能出现在头文件里。还记得#include的本质是什么吗#include只是把包含的文件内容照搬过来那么对编译器来说上面这段代码就变成了#ifndefDEFINE_H#defineDEFINE_H#includeenum.h#defineCHOOSE_10#defineCHOOSE_21#defineTESTCHOOSE_1#endif#ifndefENUM_H#defineENUM_H#includedefine.henum{state_0,#ifTESTCHOOSE_2state_test,#endifstate_what,};voidprint_what();#endif#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}这里是第一次展开因为define.h和enum.h相互包含所以还得展开一次但又因为#ifndefENUM_H#defineENUM_H#endif结构的存在每个头文件都只会被编译一次所以a.c的完全体应该是这样的#ifndefDEFINE_H#defineDEFINE_H#ifndefENUM_H#defineENUM_H// #include define.h 已经展开不再展开enum{state_0,#ifTESTCHOOSE_2state_test,#endifstate_what,};voidprint_what();#endif#defineCHOOSE_10#defineCHOOSE_21#defineTESTCHOOSE_1#endif/** 这里在上面已经被展开所以不再被展开 #ifndef ENUM_H #define ENUM_H #include define.h enum { state_0, #if TESTCHOOSE_2 state_test, #endif state_what, }; void print_what(); #endif */#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}发现了吗当你完全展开之后#define TEST CHOOSE_1都还没有定义呢那么我们追更到底此时TEST到底是多少呢怎么就满足条件TESTCHOOSE_2了。深究《C程序设计语言》中的原话是预处理器进行宏扩展之后仍然存在的任何标识符都将用0来代替也就是说还没有定义的宏就是0。那么#if TESTCHOOSE_2就被展开为#if 00,那么自然就满足条件了。但是真是这样吗我们写一个简单的程序#includestdio.hintmain(intargc,char*argv[]){#ifWHATZEROprintf(yes it is!\r\n);#endif}显然没有定义的宏编译器就是默认为0验证如果真是我们推理的这样那么把#include 的顺序改下会怎么样呢?我们把a.c改为如下#includeenum.h#includedefine.h#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}对应展开应该是#ifndefENUM_H#defineENUM_H#ifndefDEFINE_H#defineDEFINE_H//#include enum.h#defineCHOOSE_10#defineCHOOSE_21#defineTESTCHOOSE_1#endifenum{state_0,#ifTESTCHOOSE_2state_test,#endifstate_what,};voidprint_what();#endif//#include define.h#includestdio.hvoidprint_what(){printf(a:%d\r\n,state_what);}此时TEST已经定义再次运行一下这回就对了总结综上所述如果我们的头文件相互包含的时候如果存在条件编译那么在同时包含这两个头文件的c语言文件时需要十分注意头文件包含顺序。条件编译在宏未定义时默认都是0。