在这里通过简单的程序对C和C++的单定义规则进行测试和说明以加深理解。

我们知道一般头文件包含的内容有:

  • 函数原型
  • 使用#defineconst(仅对于C++而言)定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

正常在头文件中的内容是不进行内存分配的,例如若是变量则同一个变量会被重复定义声明,同理函数定义也是。所以也不能使用#include来包含源代码文件,也会导致多重声明

单定义规则(One Definition Rule, ODR)

单定义规则(One Definition Rule, ODR):变量只能有一次定义。

什么是定义?
C++提供了两种变量声明:

  1. 定义声明(defining declaration)或简称定义(definition):给变量分配存储空间。
  2. 引用声明(referencing declaration)或简称为声明(declaration):它不给变量分配存储空间,因为它引用已有的变量。

下面来简单测试下C语言和C++的单定义规则
共三个文件:

  1. test.h

    1
    2
    3
    // 定义两个常规变量
    int var1 = 1;
    double var2 = 2;
  2. file1.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "test.h"
    #include <stdio.h>
    int main(void)
    {
    printf("%d", var1);
    printf("%f", var2);
    return 0;
    }
  3. file2.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "test.h"
    #include <stdio.h>
    int main(void)
    {
    printf("%d", var1);
    printf("%f", var2);
    return 0;
    }

按照翻译单元(文件)单独编译每个文件时编译并不会出问题。但将目标文件链接时便有multiple definition的报错
(使用g++也会出现同样的错误)
因此C语言和C++都会受到单定义规则的约束。

const限定符

先上例子吧,在test.h中的变量前全部添加const限定符,分别使用gcc和g++对相应的C和C++翻译单元进行编译。
test.h

1
2
const int var1 = 1;
const double var2 = 2;

若用gcc进行编译链接后仍会报错multiple definition

但是用g++编译则没有报错

这里就可以看出:

和C语言不同,C++中,const限定符对默认的存储类型稍有影响。在默认情况下全局变量的链接性为外部的,但是用了const限定符使得全局变量的链接性为内部。也就是说在C++看来,全局const定义就像使用了static说明符一样。

1
2
3
4
5
6
const int fingers = 10;
int main()
{
...
}

相当于

1
2
3
4
5
6
static int fingers = 10;
int main()
{
...
}

这样把const常量放在头文件中,然后每个文件包含头文件也只是相当于增加了文件内部链接的常量,链接成一个程序并不会出现违背单定义规则的双重定义的情况出现。

如果处于某种原因,希望某个常量的链接性为外部的,则可以使用extern关键字来覆盖默认的内部链接性。
1
extern const int states = 50;

总结

  • C和C++中变量定义都有单定义规则的约束。
  • C++中const限定符和static功能类似,C语言中则不是。

Comments