extern
尽管大多数人可能理解变量或函数的“声明”与“定义”之间的区别,但是为了完整起见,我想对其进行澄清。
声明变量或函数仅声明变量或函数存在于程序中的某个位置,但未为它们分配内存。变量或函数的声明起着重要的作用-告诉程序其类型将是什么。如果是函数声明,它还会告诉程序参数,其数据类型,这些参数的顺序以及函数的返回类型。这就是声明。
关于定义,当我们定义一个变量或函数时,除了声明所做的一切外,它还为该变量或函数分配内存。因此,我们可以将定义视为声明的超集(或声明作为定义的子集)。
变量或函数可以声明多次,但只能定义一次。(请记住基本原则,即不能有两个位置具有相同的变量或函数)。
由于extern关键字将功能的可见性扩展到整个程序,因此该功能可以在整个程序的任何文件中的任何位置使用(调用),只要这些文件包含该功能的声明即可。(在适当的位置声明了函数,编译器知道该函数的定义存在于其他地方,并且可以继续编译文件)。这就是extern功能。
我们经常会遇到这样的问题,我们的函数在调用之后定义的,那么我们程序是不知道这个函数的存在的,也就导致这函数无法使用比如:
解决办法我们可以在文件开头对函数进行声明
extern还可以配合头文件将文件内需要的变量或函数结构体等声明出来提供给其它功能使用
.h头文件
-
在实际的开发中,我们往往需要在不同的文件中,去调用其它文件的定义的函数,前面我们已经看过 stdio.h 头文件,它是编译器自带的头文件,引用头文件相当于复制头文件的内容。
-
头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。
-
有两种类型的头文件:程序员编写的头文件和C标准库自带的头文件 ,在程序中要使用头文件,需要使用 C预处理指令 #include来引用它
-
#include 的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的时候。
-
建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件
-
源文件的名字 可以不和头文件一样,但是为了好管理,一般头文件名和源文件名一样.
-
一个 #include 命令只能包含一个头文件,多个头文件需要多个 #include 命令
-
不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不 能包含定义,否则在多次引入时会引起重复定义错误(!!!)
-
常见头文件内中包含
函数声明,变量声明,结构声明,宏声明,枚举声明,类型声明
定义头文件
如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种情况,标准的做法是把文件的整个内容放在条件编译语句中,如下:
#ifndef 头文件名 //头文件名的格式为"_头文件名_",注意要大写
#define 头文件名
头文件内容
#endif
示例代码:头文件test.h
#ifndef _MAIN_H_ //如果没有定义头文件main.h,则执行下面的代码。这是防止重复定义
#define _MAIN_H_ //定义头文件
//下面的代码是头文件的内容
#include<stdio.h>//头文件
#define ADD 1 //宏定义
extern int x; //全局变量
void swap(int a, int b);//函数声明
#endif //表示头文件结束
注意事项: 在头文件中声明变量必须需要加extern因为extern声明告诉编译器这个变量的定义在其他文件中,我在这里只是声明不使用,所以并不会为它分配内存。 而函数默认就有extern所以无须手动声明
引用方式
引用头文件的语法它的形式有以下两种:
#include <file>
这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。
#include "file"
这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。
头文件规范
- 头文件中适合放置接口的声明,不适合放置实现
- 头文件应当职责单一,切忌依赖复杂
- 头文件应向稳定的方向包含
- 每一个 .c 文件应有一个同名 .h 文件,用于声明需要对外公开的接口
- 禁止头文件循环依赖
- c/.h禁止包含用不到的头文件
- 头文件应当自包含 (自包含就是任意一个头文件均可独立编译。如果一个文件包含某个头文件,还要包含另外一个头文件才能工作的话, 就会增加交流障碍,给这个头文件的用户增添不必要的负担。)
- 禁止在头文件中定义变量,注意定义和声明式不一样的
- 使用前置声明(forward declarations)可尽量减少头文件中#include的数量,也就是能依赖声明的就不要要依赖定义。
- 只能通过包含头文件的方式使用其它.c提供的接口,禁止在.c中通过extern 的方式使用外部函数接口
- 总是编写内部 #include 保护符( #define 保护)
- 禁止在头文件中定义变量
- 只能通过包含头文件的方式使用其他 .c 提供的接口,禁止在.c 中通过 extern 的方式使用外部函数接口、变量
结构体、枚举、宏等定义在头文件.h中和定义在.c中的不同考虑
如果其可见性超出一个.c文件,那么应当放入.h中,如果只是某个.c里需要这么一个结构作为辅助,直接放入这个.c中更好一些。 .h的影响范围更大,如果有修改的话所有依赖的文件都要重新编译,会影响编译效率,主要就是基于这个考虑
假设你要和其他模块传递数据,你定义了一个结构体,然后放在common.h(假设有这个文件),别人在解析你传来的数据的时候,只需要在他的代码里#include "common.h"即可使用该结构体,试想一下,如果你放在.c文件中,别人该怎么办呢?难道#include 你的.c文件?太扯了吧。
——放c还是h取决于该结构是否要暴露给其他c,原则就是能放c绝不放h,但一般来说既然定义了结构,多半还是要暴露出来的.
extern+头文件案例
结构体+头文件案例
如果一个结构体或共用体等, 会在多个文件中使用那么我们会将他定义在头文件中,这样就可以通用了