C语言为什么一般不在.h中定义函数或者变量?(精华)

1、头文件的作用

大部分C编程爱好者都知道,在我们的.h文件里面经常看到的是函数的声明、变量的声明、以及各种各样的宏等等,而且在我前面的文章中我也提到过C语言的模块化设计中常常说到对应的.h和.c文件认为是一个对象,那么.h文件主要是对外的一些接口,一些私有的数据等等实现都会封装在我们的.c中,如果更加形象一点的说明我们的.h,就相当于文章下面的推荐文章链接组成的框,点进去就到了对应的具体文章,具体的文章也就是我们的.c文件了。

这里再回到我们的程序中来,如下代码所示:

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. int sAdd(int a,int b);

  4. int sSub(int a,int b);

  5. //...

  6. int main(int argc, char *argv[]) {

  7. printf("Add: %d\n",sAdd(1 , 1));

  8. printf("Sub: %d\n",sSub(1 , 1));

  9. printf("公众号:最后一个bug\n");

  10. return 0;

  11. }

  12. /***************************************

  13. * Fuction:sAdd

  14. * Author : (公众号:最后一个bug)

  15. ***************************************/

  16. int sAdd(int a,int b)

  17. {

  18. return (a + b);

  19. }

  20. /***************************************

  21. * Fuction:sSub

  22. * Author : (公众号:最后一个bug)

  23. ***************************************/

  24. int sSub(int a,int b)

  25. {

  26. return (a - b);

  27. }

  28. //...

解析一下: 上面是大部分小伙伴最开始学习编程的时候做法,那么我们把main函数前面的声明放到一个对应的.h文件,加法与减法的具体实现放到对应的.c文件,然后再main函数前面用#include "xxx.h",便可以了。简单点说.h完全可以直接在#include处进行展开以便于我们理解分析。

2、头文件中定义函数或者变量

代码阅读量比较多的小伙伴应该见过直接在头文件里面定义函数或者变量的做法,那么这里我们直接对上面进行改造,得到如下代码:

  1. //Filename: main.c

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include "Add.h"

  5. int main(int argc, char *argv[]) {

  6. printf("Add: %d\n",sAdd(global_Var1 , global_Var2));

  7. printf("Sub: %d\n",sSub(global_Var1 , global_Var2));

  8. printf("公众号:最后一个bug\n");

  9. return 0;

  10. }

 
  
  1. //Filename : Add.h

  2. /***************************************

  3. * Fuction:sAdd

  4. * Author : (公众号:最后一个bug)

  5. ***************************************/

  6. int global_Var1 = 2;

  7. int global_Var2 = 2;

  8. /***************************************

  9. * Fuction:sAdd

  10. * Author : (公众号:最后一个bug)

  11. ***************************************/

  12. int sAdd(int a,int b)

  13. {

  14. return (a + b);

  15. }

  16. /***************************************

  17. * Fuction:sSub

  18. * Author : (公众号:最后一个bug)

  19. ***************************************/

  20. int sSub(int a,int b)

  21. {

  22. return (a - b);

  23. }

解析一下:上面的程序也能够成功编译并运行,并且能够获得正确的结果,这也进一步证明直接展开#include "xxx.h"是可以解释得通的。

3、为什么不选择在头文件定义变量或者函数

1)多个源文件声明.h会重复定义 

我们如果按照上面的展开法进行解释,其实很好理解多个源文件声明#include "xxx.h"会导致多个相同的全局变量和函数,这样必然导致重复定义故障,如果小伙伴们不信我直接敲代码看结果:

 
  
  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include "APP1.h"

  4. #include "APP2.h"

  5. /*********************************************

  6. * Fuction:main

  7. * Author : (公众号:最后一个bug)

  8. ********************************************/

  9. int main(int argc, char *argv[]) {

  10. printf("%d\n",add_APP1(1,1));

  11. printf("%d\n",add_APP2(1,1));

  12. return 0;

  13. }

 
  
  1. //Filename:Add.h

  2. /*********************************************

  3. * Fuction:add

  4. * Author : (公众号:最后一个bug)

  5. ********************************************/

  6. int add(int a,int b)

  7. {

  8. return (a + b);

  9. }

 
  
  1. //Filename:App1.h

  2. int add_APP1(int a,int b);

 
  
  1. //Filename:App1.c

  2. #include "Add.h"

  3. /*********************************************

  4. * Fuction:add_APP1

  5. * Author : (公众号:最后一个bug)

  6. ********************************************/

  7. int add_APP1(int a,int b)

  8. {

  9. return add(a,b);

  10. }

 

解析:APP2对应的.c和.h我就不再贴上去了,毕竟代码比较简单。最后一张图为编译结果,第二行显示重复定义add这个函数。同样作者也进行了对变量一样的做法,同样也是显示的是重复定义相关变量,所以也验证了我们的前面的理论。

2)添加条件编译#ifndef 

我们大部分小伙伴可能会想我们直接在头文件的函数和变量定义加上条件编译#ifndef来避免重复包含不就可以了吗?好了,作者也进行了这个实验大家简单看看代码和结果,后面我来解释下:

  1. //Filename:Add.h

  2. /*********************************************

  3. * Fuction:add

  4. * Author : (公众号:最后一个bug)

  5. ********************************************/

  6. #ifndef __ADD_H__

  7. #define __ADD_H__

  8. int add(int a,int b)

  9. {

  10. return (a + b);

  11. }

  12. #endif

解析一下:加了该预编译以后其结果与上一节的结果一致,还是提示重复定义该函数。那是为什么呢?我在前面的《嵌入式编程有道-C语言(1)》中有描述C程序到机器码的一个生成过程,App1.c和App2.c分别编译,然后分别展开#include,在最后的链接过程中发现了同名的函数和变量,所以还是会编译不通过。

3)static能够帮我们搞定这种窘境 

我们都知道static表示静态的的意思,也可以说是对变量和函数进行文本上的限制,多个文件中是可以有同名的static函数和变量的,不过他们却代表着不同的函数和变量,这一点要尤为注意,如果该头文件在不同文件中大量使用其中的变量和函数也会导致程序的增加和内存的变大,下面作者就为这几点用程序来证明下:

 
  
  1. //Filename:Add.h

  2. #ifndef __ADD_H__

  3. #define __ADD_H__

  4. static int add(int a,int b)

  5. {

  6. return (a + b);

  7. }

  8. #endif

 
  
  1. //filename:App1.c

  2. #include <stdio.h>

  3. #include "Add.h"

  4. /*********************************************

  5. * Fuction:add_APP1

  6. * Author : (公众号:最后一个bug)

  7. ********************************************/

  8. int add_APP1(int a,int b)

  9. {

  10. printf("add_APP1_addFuc:0x%x\n",(int*)add);

  11. return add(a,b);

  12. }

 

 解析一下:上面仅仅贴了重要的两段,代码比较简单,我们发现两个里面都是调用的add,但是他们的地址是不相同的,说明他们并不是一个函数。所以大家也可以把Add.h文件用我们模块化设计的思想分为一个.c和一个.h的形式,最后打印的地址是一样的,代码我就不贴了给大家看一下结果即可,地址是一样的说明他们是同一个函数,同样大伙也可以用static修饰变量,也是得到类似的结果。

4、小节

最后小节一下,其实为什么大家一般不在.h文件里面定义变量或者函数其实原因还有很多,最主要的是不符合相关设计要求,比如进行代码上的改动话,编译会造成连锁反应导致编译时间再次编译时间较长等问题,同时代码上的分析比较麻烦,所以大家对于这类问题其实也是可以平时闲暇时候好好研究一下,同时你会在其中收获很多意外的知识点,要做到"知其然而知其所以然",其实在头文件中定义函数或者变量并不是没有这样的设计需求,大家可以考虑一下static在头文件中定义的这种使用有缺点,是否会有什么优点呢?哈哈,这里我就不揭秘了,请听下回分解!


资源交流获取关注公众号:奇牛编程

猜你喜欢

转载自blog.csdn.net/weixin_55751709/article/details/129567452