怎么写头文件:内部链接、外部链接
由内部链接、外部链接引出怎么写头文件。
我看了一下,这篇笔记最早是在六月二号创建的,可是现在 2016/8/13 16:12:05 ,呵呵。一方面是自己懒,另一方面,对于“内部链接、外部链接”的概念,不多了解一些,不敢随便整理啊。
我们先看看 维基百科- 头文件 中说了什么:
当一个子程序在定义的位置以外的地方被使用时,就需要进行前置声明,声明函数原型等。
假设一个程序员不用“头文件”,那么他至少需要在两个地方维护函数的声明:一个是包含函数实现的的文件,以及使用该函数的文件。如果使用该函数的文件有很多个,那么对函数的定义进行更改时就是灾难。
从某个方面来说,头文件降低了这种场景中程序员手工操作的复杂度(解放双手,繁琐的工作交给机器/编译器)。更重要的是保证了编写大型项目的易用性,难以想象如果没有头文件,几十万行的代码全都在一个源文件中。
将函数原型移到 XXX 头文件中之后,我们可以在需要的地方通过 #include <XXX>
预处理器指令 将其包含进来,这样每次编译时预处理阶段就会将 XXX 文件中的内容替换掉 #include <XXX>
,我们的函数原型也就被“前置声明”了。
阅读 wikipedia - include derective,我们可以从更高的层次考虑 Include 行为,想想 Makefile 中的 include,shell 脚本中的 include,表示类似甚至同样意义的关键字还有 import、copy。
在 C 语言中,什么内容需要放在头文件中,什么内容可以放在头文件中相对来说是比较容易区分的。接下来我们看看在 C++ 中,什么东西可以放在 .h 文件中,什么不能,什么东西又可以放在 .cpp 文件中。
声明和定义
首先需要区分开这两个概念,只是理解所有关键问题的前提。在笔记中暂时不展开说了,如果分不清楚,自行 Google。这里只备注几个容易混淆的条目:
声明
- 仅仅提供函数原型。类外面的,类里面的
- class A;
- typedef声明
- 在类中定义的静态数据成员的声明
1
2
3
4
5class A
{
public:
static int a; // 声明
};定义
- 在类定义之外,定义并初始化一个静态数据成员。如 A::a=0;
1
2
3
4
5
6class A
{
public:
static int a; // 声明
};
A::a=0; // 定义- 在类外定义非内联成员函数
内部链接和外部链接
链接把不同编译单元产生的符号联系起来。有两种链接方式:内部链接和外部链接。
内部链接
如果一个符号名对于它的编译单元来说是局部的,并且在链接时不可能与其他编译单元中的同样的名称相冲突,那个这个符号就是内部链接。内部链接意味着对此符号的访问仅限于当前的编译单元中,对其他编译单元都是不可见的。
具有内部链接的符号无法作用于当前文件外部,要让其影响程序的其他部分,可以将其放在.h文件中。此时在所有包含此.h文件的源文件都有自己的定义且互不影响。
所有的声明,包括类的声明,比如:
class A;
;(有时也将声明看作是无连接的,这里我们统一看成是内部连接的)。由于声明只对当前编译单元有用,因此声明并不将任何东西写入.o文件。
这些声明本身不会影响到.o文件的内容。在编译阶段(狭义的,从高级语言到汇编语言到二进制,从 main.i 到 main.o,预处理已过,尚未链接),编译器只检测程序语法和函数、变量是否被声明。如果函数未被声明,编译器会报错。链接阶段,声明已经没有用了。
而函数调用会导致一个未定义的符号被写入到.o文件,此后此.o文件与定义此符号的.o文件被连接在一起,前面未定义的符号被解析。
局部变量肯定是内部链接性质的,更应该被看作无连接的;
全局变量,如果使用了 static、const 关键词修饰,其作用域仅仅在当前文件作用域内,其他文件中即使使用extern声明也是无法使用的。因此,带有 static、const 关键字的全局变量也是内部链接性质的;
static 和 const 还是有区别的。static 和 extern不能对同一个变量同时声明;但 const 和 extern 不是同一存储类别,可以同时用在同一个变量的声明。所以,我们可以用使用extern关键字修改const的连接属性。更多详情、特别说明
枚举 enum、联合 union 类型是内部链接性质的;
类的定义是内部链接性质的:
定义,意味着在同一编译单元中不能重复出现;
内部链接性质,意味着如果需要在其他编译单元使用,类必须被定义在头文件且被其他文件包含。
仅仅在其他文件中使用class a;声明是不行的,原因就是类的定义是内部链接,不会在目标文件导出符号。也就不会被其他单元解析它们的未定义符号。
内联函数定义(包括自由函数和非自由函数)。
内联函数之所有具有内部链接,因为编译器在可能的时候,会将所有 对函数的调用替换为函数体,不将任何符号写入.o文件。
名字空间(包括全局名字空间)中的静态自由函数、静态友元函数、静态变量的定义。补充条目来源
外部链接
在一个多文件的程序中,如果一个符号在链接时可以和其他编译单元交互,那么这个名称就有外部链接。外部链接意味着该定义不仅仅局限在单个编译单元中。它可以在.o文件中产生外部符号。可以被其他编译单元访问用来解析它们未定义的符号。因此它们在整个程序中必须是唯一的,否则将会导致重复定义。
区分:判断一个符号是内部链接还是外部链接的一个很好的方法就是看该符号是否被写入.o文件。
类非内联成员函数,包括类成员函数和类静态成员函数
类的静态数据成员的定义具有外部链接性质
1
2
3
4
5
6
7class A
{
public:
static int a; // 内部链接
};
A::a = 0; // 外部链接非内联函数
名字空间(包括全局名字空间)中非静态自由函数、非静态友元函数及非静态变量。补充条目来源
前提是认为,类之外一般不存在使用 inline 修饰的函数。
学习
Linkage
To understand the behavior of C and C++ programs, you need to know about linkage. In an executing program, an identifier is represented by storage in memory that holds a variable or a compiled function body. Linkage describes this storage as it is seen by the linker. There are two types of linkage: internal linkage and external linkage.
Internal linkage means that storage is created to represent the identifier only for the file being compiled. Other files may use the same identifier name with internal linkage, or for a global variable, and no conflicts will be found by the linker – separate storage is created for each identifier. Internal linkage is specified by the keyword static in C and C++.
External linkage means that a single piece of storage is created to represent the identifier for all files being compiled. The storage is created once, and the linker must resolve all other references to that storage. Global variables and function names have external linkage. These are accessed from other files by declaring them with the keyword extern. Variables defined outside all functions (with the exception of const in C++) and function definitions default to external linkage. You can specifically force them to have internal linkage using the static keyword. You can explicitly state that an identifier has external linkage by defining it with the extern keyword. Defining a variable or function with extern is not necessary in C, but it is sometimes necessary for const in C++.
Automatic (local) variables exist only temporarily, on the stack, while a function is being called. The linker doesn’t know about automatic variables, and so these have no linkage.
结论
综上,我们可以知道:将具有外部链接的定义放在头文件中几乎都是编程错误。因为如果该头文件中被多个源文件包含,那么就会存在多个定义,链接时就会出错。
在头文件中放置内部链接的定义却是合法的,但不推荐使用的。
链接,本质上都是具有外部链接性质的符号们的事情!
惯例
头文件为相关声明提供了一个集中存放的位置。头文件一般包含类的定义、枚举的定义、extern变量的声明、函数的声明、const int的定义、inline函数的定义。使用或者定义这些实体的文件要包含适当的头文件。