针对许多人对于C语言和C++预编译宏指令
#ifndef XXX
#define XXX
代码片
#endif
的作用存在误解或一知半解的状况,本文试图从编译链接的角度对以上宏指令给出清晰全面的解读。
该宏指令一般位于工程文件的头文件中。编译程序的第一步是执行预编译过程,逐个将工程内所有.c文件中以#开头的语句按照该预编译指令的含义替换为相应的代码。
例如,在cjx.c中,一条宏指令语句为#include”cjx.h”,则根据含义,编译器找到cjx.h,将其中的代码复制到cjx.c中该位置替换掉宏指令。然而cjx.h开篇即是#ifndef ZY,此时编译器会检查宏名ZY是否在之前已定义(#define ZY),若已定义则忽略#ifndef~#endif之间所有代码,若未定义则复制代码。
实际上上述说法并不准确,其一,重复定义的判定标准依编译器而定,对于有些编译器而言,语法上出现多重定义时,会自动把多余的定义看作声明。其二,在头文件中定义变量并不是一种良好的编程习惯,我们总是只在头文件中声明函数,另外新建一个同名的.c文件来实现这些内容。
从另一方面来讲,由于上述引发错误的情境鲜有发生(这都能写错太可怕了),所以我们可能会认为#ifndef没有多大的实际作用,事实的确如此。然而在某些情境下,我们必须在头文件中定义一些变量和结构体,以便其他文件在包含该头文件后可以直接使用它们。(题外话:若在.c文件中使用其他.c文件定义的变量、函数等,必须加关键词extern显式声明。)。考虑以下情境:cjx.h中定义了变量int i, xwb.h包含了cjx.h,main.c包含了xwb.h和cjx.h,则#ifndef帮助我们避免了int i的重复定义。但是,如果cjx.h的实现文件cjx.c(包含cjx.h)中定义了一个函数farewell( ),在main.c中有调用这个函数,那么在链接过程中,链接器会在链接前分别检查main.o和cjx.o,发现int i在两个文件中都有定义,将抛出链接时错误:variable i multi defined in both main.o and cjx.o。可见,#ifndef并不能阻止此类错误。