一直以为对C很了解,不想今天犯了个错误:
在C头文件中,预处理宏定义的作用是防止(头文件)错误的包含关系。具体来说,比如有两个头文件a.h,b.h,其中b.h又包含a.h。但若在另一个文件中同时包含这两个头文件的话(理应只包含头文件b.h,但用户可能以为它们二者是不相关的),若没有在a.h中作预处理宏定义的话,a.h文件中定义的变量,函数将会出现“重复定义”的错误。
举例如下:
相互包含关系如下:
此时编译会报如下错误:
原因是在“编译”x.c源文件时,同时包含a.h b.h导致函数yourPrint() 两次展开,直接造成重定义。
然后,据此,改a.h如下:(同理b.h最好也应按如下改,不然会有c.h, d.h 。。。)
此时编译又会报如下错误:
很明显,是链接时出错:yourPrint( ) 又被重定义了,我不是添加了宏定义预处理了吗????
再仔细看上述标黑的第一行:链接错误,在链接a这个模块(C语言编译单位是源文件)时, 在a.c中展开a.h时,发现yourPrint( ) 已经在x.c这个模块中定义(展开)过了。
那我添加的宏定义预处理是不是不起作用?
不,已经起作用了,但它的作用仅限于本文开始所述。 也就是说,模块间编译时,单个文件中的预处理宏定义,不会影响到其它模块。就本例来说:在x.c中展开a.h, b.h时,在展开a.h时,a.h由于是第一次被展开,_A_H_没有被定义过,所以a.h被完全展开;当展开b.h时,在x.c中,a.h已被展开过,即_A_H_已被定义过,所以b.h由于单纯地包含a.h,故不会展开。所以这时,第一个错误消失了。
但到了第二个模块a.c时。当a.h已被展开时,由于_A_H_并未在模块a中被定义过,所以照样展开,编译是没有错误的。但链接时,发现这里再次被展开的yourPrint( )已经在另一模块x中定义了,所以错误2产生了。。。
解决方法,当然是仿myPrint(),不在头文件中定义函数实现啊。
弄清楚这个原因后,得到两点结论:
1.头文件中的预处理宏定义只对单个模块在编译时是否展开判断起作用;
2. 在头文件中不要去实现函数,因为重复声明是允许的,但“被”重复定义就会链接出错。