C头文件声明和实现分开 from CSDN

类声明放到.h文件
实现放到cpp文件

头文件(.h)主要包含函数的声明、宏定义等源文件(.cpp)是函数 的实现部分 是主体部分。


在C语言里面,有时候为了方便(方便的同义词是偷懒),函数就直接在头文件里面实现了。那么这样子有什么问题呢?
下面举个例子,这个例子只有3个文件

  1. </pre><pre name="code" class="cpp">#ifndef FUN_H  
  2. #define FUN_H  
  3. void base(){};  
  4. void fun();  
  5. #endif  
  6.   
  7. #include "fun.h"  
  8. void fun()  
  9. {  
  10. base();  
  11. }  
  12.   
  13. #include "fun.h"  
  14. int main()  
  15. {  
  16. fun();  
  17. return 0;  
  18. }  





好,然后gcc一下
gcc -c main.c (通过)
gcc -c fun.c (通过)
gcc -o main main.o fun.o (链接错误)
出现错误...“base()函数重定义!”




为什么重定义呢?因为#include是预处理部分,在编译之前由预处理程序在这个部分复制头文件的内容过来。所以在编译时候,main.o和fun.o文件都有base()函数的定义。那么链接程序就不知道链接那个定义好了(二义性啊)
如何解决呢,为了实现“声明和实现分开”这个目标最好就是把这个base函数的函数体移到源文件里面。如果由于某种原因真的要放在头文件中...也可以。
用static声明就可以了,静态函数的作用域是文件,而不是全局。比如,
上面的例子将头文件里面的 void base(){}改成static voidbase(){} ,那就OK。这个static在c语言中的用法可以google下,上面的资料好多很详细滴。



顺便说说头文件的循环依赖的问题。
比如有三个头文件a.h b.h c.h,a.h里面有#include "b.h",b.h里面有#include "c.h",c.h里面有#include "a.h",那就会造成文件的循环依赖,后果是什么呢?
比如有个文件a.c,上面有#include"a.h",那在a.c文件编译之前,预处理程序就会不断的把这三个头文件的内容复制过来,超过了一定的数量,就会导致“头文件数太多”的编译错误。


解决方法呢,当然就是常见的#ifndef...#define...#endif组合了。不过要把 前两个 写在头文件的开头(一定是开头),最后一个写在最末尾。


这样的话,第一次展开a.h b.h c.h的时候就已经定义了宏,到了c.h中的#include"a.h"时候,遇到了#ifndef,由于这个宏在上一次展开时已经定义了,所以这部分就跳过去了。也就是每个头文件最多只在每个源文件里面包含一次。
但是即使编译链接没有问题,循环依赖也会降低开发效率,为什么?因为文件都在依赖,比如某一天,要改变a.h的一部分内容,然后所有依赖于a.hb.hc.h的文件都得重新编译...链接;所以现在的C++有“前向声明”的技巧可以缓解这个问题。(缓解并不是解决。)而JAVA运用的import机制就很好的解决了这个问题,真正实现了“实现与声明相分离”这个目标。

 
 
  1. /*zyrandom.h*/  
  2. #ifndef __ZYRANDOM__  
  3. #define __ZYRANDOM__  
  4. void Randomize();  
  5. #endif  
  6.   
  7. /*zyrandom.c*/  
  8. #include <stdlib.h>  
  9. #include <time.h>  
  10. #ifndef __ZYRANDOM__  
  11. #include "zyrandom.h"  
  12. #endif  
  13. void Randomize()  
  14. {  
  15.      srand((int)time(0));  
  16. }  
  17.   
  18. /*main.c*/  
  19. #include <stdio.h>  
  20. #include <stdlib.h>  
  21. #include "zyrandom.h"  
  22.   
  23. int main()  
  24. {  
  25.     int a;  
  26.     Randomize();  
  27.     a=rand();  
  28.     printf("%d\n",a);  
  29.     system("pause");  
  30.     return 0;  
  31. }  
把上面三个文件放在一个工程之中


1.为什么头文件"zyrandom.h"只字未提它的源文件"zyrandom.c",它也能找到里面的函数的定义
2.源文件"zyrandom.c"里面的这3行代码有实际意义吗?
#ifndef __ZYRANDOM__
#include "zyrandom.h"
#endif
3.使用条件编译的目的之一就是避免重复定义,那么问什么"main.c"和"zyrandom.c"里面重复包含了<stdlib.h>也能正确编译呢?



 回答:
1,zyrandom.h文件声明函数void Randomize();的时候,其实默认关键字是extern,代表这函数可能在别的文件中定义。所以.h中是声明,而.c中是定义,所以包括.h文件的话,自然能找到函数的定义。
2,我感觉意义不大(我的看法,也许不是太对,但要是我写的话肯定不这么写,因为它要表达的意思很不好,给人误解)
3,它没有重复包含,main只包含了zyrandom.h,并未包含zyrandom.c啊。
不过我感觉毕竟是标准库,重复包含了也不一定错,这点你可以试一下啊,咱虽然尽量避免这种可能的错误,但可以试一下到底是怎么回事,是会错误,还是没问题,还是编译器相关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值