在头文件被重复引入时(间接地,或直接地,被include了两次),如果不加这个,就会导致头文件里的内容,被引入两次;加了这个之后呢,即使被include了两次,程序在运行时,一开始,发现没有定义__REDIS_H这个宏,然后定义它;等到程序遇到第二次include的内容时,发现__REDIS_H这个宏已经被定义了,就直接跳过了,这样保证了同一个头文件,即使被多次include,也能保证其内容,只被解析一次。
另外,像方法声明这种,定义多次可能没事,但是,如果在头文件里,有如下类型定义呢:
typedef char my_char; char *test (void);如果重复include同一个头文件的话,就会造成类型重复定义。不过,很奇怪的是,我在centos 7.3.1611上试了,gcc版本:gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16),竟然没报错。看来我之前的c语言知识,也没学到家。
我在网上暂时也没找到重复include,具体的害处是啥,网上找到的答案就两种:
在header文件里定义了全局变量;
浪费编译时间
但是,第一个答案,严格来说 ,是不存在的,因为公司一般禁止在头文件中定义变量。
有个知乎问题,大家可以看看:头文件被重复包含究竟有哪些危害?
华为c语言编程规范中,对头文件的部分规定大家可以自行搜索:华为技术有限公司c语言编程规范
我这里仅截取部分:
规则1.6 禁止在头文件中定义变量。 说明: 在头文件中定义变量,将会由于头文件被其他.c文件包含而导致变量重复定义。 规则1.7 只能通过包含头文件的方式使用其他.c提供的接口,禁止在.c中通过extern的方式使用外部 函数接口、变量。 说明:若a.c使用了b.c定义的foo()函数,则应当在b.h中声明extern int foo(int input);并在a.c 中通过#include <b.h>来使用foo。禁止通过在a.c中直接写extern int foo(int input);来使用foo, 后面这种写法容易在foo改变时可能导致声明和定义不一致。这里的1.7,也是和我们的理解是一致的,头文件就是一个实现模块的对外接口,在里面一般只能允许放以下内容:
类型定义
宏定义
函数的声明(不包括实现)
变量的声明(不是定义)
最后这一点,我要补充下。我们刚才禁止了,在头文件中定义变量,所以,我们的变量,是在c文件中定义。比如,在redis.c中,定义了一个全局变量:
/* Global vars */ struct redisServer server; /* server global state */这么一个重要的全局变量,基本维护了redis-server的一个实例的全部状态值,只在自己redis.c中使用,是不可能的。那要怎么在其他文件使用呢,就要在redis.h头文件中进行如下声明:
/*----------------------------------------------------------------------------- * Extern declarations *----------------------------------------------------------------------------*/ extern struct redisServer server; 关于类型定义一般使用struct来定义一个结构体,类似高级语言中的class。
比如,redis中的字符串,一般会使用sds这个数据结构来存储,其结构体定义就像下面这样:
struct sdshdr { // buf 中已占用空间的长度 int len; // buf 中剩余可用空间的长度 int free; // 数据空间 char buf[]; };另外,c语言中,会大量使用typedef来定义一个类型的别名。
具体可以参考这个教程看看:
https://www.runoob.com/cprogramming/c-typedef.html
关于指针基础知识:https://www.runoob.com/cprogramming/c-pointers.html
我这里说下我对指针的理解,指针一般指向一个内存地址,大家可以先不管这个指针是什么类型,事实上,当我们不关心其指向的地址上,是什么数据类型时,可以直接定义为 void * ptr。
这个指针,假设指向A这个地址,当我们认为上面存储的是一个char时,就可以把这个指针,从void *强转为char * 类型,然后对该指针解引用的话,因为char类型只占用一个字节,所以只需要,从该指针指向的位置开始,取当前这个字节的内容,然后解析为char,就能获取到这个地址上的char值。
如果我们把void * 强转为int *的话,对其解引用时,就会取当前指针位置开始的4个字节,因为整数占4个字节,然后将其转为整数。