单片机技术网|技术阅读
登录|注册

您现在的位置是:单片机技术网 > 技术阅读 > C语言为什么一般不在.h中定义函数或者变量?

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

今天分享的是一个大家可能不太关注的主题,但是讨论起来还是非常有趣的,虽然大部分C语言模块设计开发者们早就形成了习惯,一般不会在.h文件中定义变量和函数,也就不会去关注这一块的内容,不过我们今天想拿出来探个究竟.

1、头文件的作用

 大部分C编程爱好者都知道,在.h文件里面经常看到的是函数、变量的声明、以及各种各样的宏等等。

并且在我前面的文章中也提到过C语言的模块化设计中常常说到用对应的.h.c文件来封装一个对象,那么.h文件主要是对外的一些接口,一些私有的数据等等实现都会封装在我们的.c源文件中。

如果需要更加形象一点的说明.h头文件,就相当于文章下面的推荐文章链接,点进去就到了对应的具体文章内容,具体的文章也就是.c原文件了。

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

1#include <stdio.h>
2#include <stdlib.h>
3int sAdd(int a,int b);
4int sSub(int a,int b);
5//...
6int main(int argc, char *argv[]) 
7{
8    printf("Add:  %d\n",sAdd(1 , 1));
9    printf("Sub:  %d\n",sSub(1 , 1));
10    printf("公众号:最后一个bug\n");
11    return 0;
12}
13
14/***************************************
15 * Fuction:sAdd
16 * Author : (公众号:最后一个bug)
17 ***************************************/

18int sAdd(int a,int b) 
19{    
20    return (a + b); 
21}
22
23/***************************************
24 * Fuction:sSub
25 * Author : (公众号:最后一个bug)
26 ***************************************/

27
28int sSub(int a,int b) 
29{    
30    return (a - b);
31}
32//...

解析一下: 上面是大部分朋友最开始学习编程的时候做法,那么把main函数前面的声明放到一个对应的.h文件,加法与减法的具体实现放到对应的.c文件,然后在main函数前面加入#include "xxx.h",便可以了。

简单点说.h完全可以直接在#include处进行展开以便于理解分析。

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

代码阅读量比较多的朋友应该见过直接在头文件里面定义函数或者变量的做法。

那么这里直接对上面进行改造,得到如下代码:

1//Filename: main.c
2#include <stdio.h>
3#include <stdlib.h>
4
5#include "Add.h"
6
7int main(int argc, char *argv[]) 
8{
9    printf("Add:  %d\n",sAdd(global_Var1 ,global_Var2));
10
11    printf("Sub:  %d\n",sSub(global_Var1 , global_Var2));
12
13    printf("公众号:最后一个bug\n");
14
15    return 0;
16}

1//Filename : Add.h
2/***************************************
3 * Fuction:sAdd
4 * Author : (公众号:最后一个bug)
5 ***************************************/

6
7int global_Var1 = 2;
8int global_Var2 = 2;
9
10/***************************************
11 * Fuction:sAdd
12 * Author : (公众号:最后一个bug)
13 ***************************************/

14int sAdd(int a,int b)
15{    
16    return (a + b);
17}
18
19/***************************************
20 * Fuction:sSub
21 * Author : (公众号:最后一个bug)
22 ***************************************/

23int sSub(int a,int b) 
24{    
25    return(a - b);
26}

解析一下:上面的程序也能够成功编译并运行,并且能够获得正确的结果,这也进一步证明直接展开#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/*********************************************
7 * Fuction:main
8 * Author : (公众号:最后一个bug)
9 ********************************************/

10
11int main(int argc, char *argv[]) 
12{
13    printf("%d\n",add_APP1(1,1));
14
15    printf("%d\n",add_APP2(1,1));
16
17    return 0;
18}


1//Filename:Add.h
2/*********************************************
3 * Fuction:add
4 * Author : (公众号:最后一个bug)
5 ********************************************/

6
7int add(int a,int b)
8{    
9    return (a + b);
10}

1//Filename:App1.h
2int add_APP1(int a,int b);

1//Filename:App1.c
2#include "Add.h"
3
4/*********************************************
5 * Fuction:add_APP1
6 * Author : (公众号:最后一个bug)
7 ********************************************/

8
9int add_APP1(int a,int b)
10{    
11    return add(a,b);
12}



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

2)添加条件编译#ifndef 


大部分朋友可能会想直接在头文件的函数和变量定义加上条件编译#ifndef来避免重复包含不就可以了吗?

好了,我也进行了这个实验,大家简单看看代码和结果,后面我来解释下: 1//Filename:Add.h
2
3/*********************************************
4 * Fuction:add
5 * Author : (公众号:最后一个bug)
6 ********************************************/

7
8#ifndef __ADD_H__
9#define __ADD_H__
10
11int add(int a,int b)
12{    
13    return (a + b);
14}
15
16#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
5static int add(int a,int b)
6{
7    return (a + b);
8}
9
10#endif

1//filename:App1.c
2#include <stdio.h>
3#include "Add.h"
4
5/*********************************************
6 * Fuction:add_APP1
7 * Author : (公众号:最后一个bug)
8 ********************************************/

9
10int add_APP1(int a,int b)
11{
12    printf("add_APP1_addFuc:0x%x\n",(int*)add);
13    return add(a,b);    
14}



解析一下:上面仅仅贴了重要的两段,代码比较简单,发现两个里面都是调用的add,但是他们的地址是不相同的,说明他们并不是一个函数。

所以大家也可以把Add.h文件用模块化设计的思想分为一个.c和一个.h的形式,最后打印的地址是一样的,代码我就不贴了给大家看一下结果即可,地址是一样的说明他们是同一个函数,同样大伙也可以用static修饰变量,也是得到类似的结果。



4、最  后 

最后,为什么一般不在.h文件里面定义变量或者函数其实原因还有很多,最主要的是不符合相关设计要求,比如进行代码上的改动话,编译会造成连锁反应导致再次编译时间较长等问题。

同时代码上的分析比较麻烦,所以对于这类问题其实也是可以平时闲暇时候好好研究一下,会在其中收获很多意外的知识点,"知其然而知其所以然"。

其实在头文件中定义函数或者变量并不是没有这样的设计需求,大家可以考虑一下static在头文件中定义的这种使用有缺点,是否会有什么优点呢?哈哈,这里我就不揭秘了,请听下回分解!

本文来源于:最后一个bug,版权归原作者所有。

小哥搜集了一些嵌入式学习资料,公众号内回复"1024"即可找到下载链接!

推荐好文  点击蓝色字体即可跳转

☞ 

☞ 

☞ 

☞ 

☞ 

☞ 

☞