章节目录

外部链接和变量前向声明

本节阅读量:

在上一课中,我们讨论了内部链接如何将标识符的使用限制在单个文件。在本课中,我们将探索外部链接的概念。

具有外部链接的标识符既可以从定义它的文件中看到,也可以从其他代码文件中使用(通过前向声明)。在这种意义上,具有外部链接的标识符是真正的“全局”标识符,因为它们可以在程序中的任何位置使用!


默认情况下,函数具有外部链接

在前面多个代码文件的程序中,可以从一个文件调用在另一个文件中定义的函数。这是因为函数在默认情况下具有外部链接。

为了调用在另一个文件中定义的函数,必须在使用该函数的任何其他文件中放置该函数的前向声明。前向声明将函数的存在告知编译器,链接器将函数调用连接到实际的函数定义。

下面是一个示例:

a.cpp:

1
2
3
4
5
6
#include <iostream>

void sayHi() // 这个函数有外部链接,其它文件中可以使用
{
    std::cout << "Hi!\n";
}

main.cpp:

1
2
3
4
5
6
7
8
void sayHi(); // 函数 sayHi 的前向声明, 让 sayHi 可以在这个文件中使用

int main()
{
    sayHi(); // 调用其它文件中定义的函数, 链接器会连接到实际的函数定义

    return 0;
}

上述程序打印:

1
Hi!

在上面的示例中,main.cpp中函数 sayHi() 的前向声明,允许main.cpp访问在a.cpp.中定义的 sayHi() 函数。前向声明满足了编译器的要求,并且链接器能够将函数调用链接到函数定义。

如果函数 sayHi() 具有内部链接,则链接器将无法将函数调用连接到函数定义,并将导致链接错误。


具有外部链接的全局变量

具有外部链接的全局变量有时称为外部变量。要将全局变量设置为外部变量(因此可由其他文件访问),可以使用extern关键字执行此操作:

1
2
3
4
5
6
7
8
9
int g_x { 2 }; // 非常量全局变量默认是 外部链接

extern const int g_y { 3 }; // const 全局变量可以使用extern, 设置为外部链接
extern constexpr int g_z { 3 }; // constexpr 全局变量可以使用extern, 设置为外部链接 (但这是无用的定义, 详情见下述内容)

int main()
{
    return 0;
}

默认情况下,非常量的全局变量是外部链接(添加extern关键字也会被忽略)。


通过extern关键字进行变量前向声明

要实际使用在另一个文件中定义的外部全局变量,还必须在希望使用该变量的任何其他文件中放置全局变量的前向声明。对于变量,也可以通过extern关键字(没有初始化值)创建前向声明。

下面是使用变量前向声明的示例:

a.cpp:

1
2
3
// 全局变量定义
int g_x { 2 }; // 非常量全局变量默认是 外部链接
extern const int g_y { 3 }; // const 全局变量可以使用extern, 设置为外部链接

main.cpp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <iostream>

extern int g_x; // 这里的extern,表示 g_x 是在其它地方定义的全局变量,这里做一个前向声明
extern const int g_y; // 这里的extern,表示 g_x 是在其它地方定义的 const 全局变量,这里做一个前向声明

int main()
{
    std::cout << g_x << ' ' << g_y << '\n'; // 打印 2 3

    return 0;
}

在上面的示例中,a.cpp和main.cpp都引用了名为g_x的相同全局变量。因此,即使g_x是在a.cpp中定义和初始化的,我们也可以通过g_x的前向声明在main.cpp内使用它的值。

请注意,extern关键字在不同的上下文中具有不同的含义。在某些上下文中,extern意味着“为该变量提供外部链接”。在其他上下文中,extern意味着“这是在其他地方定义的外部变量的前向声明”。是的,这很令人困惑,因此我们在后续课程中总结了所有这些用法——作用域、存储期和链接。

注意,函数前向声明不需要extern关键字——编译器能够根据是否提供函数体来判断您是在定义新函数还是在进行前向声明。变量前向声明确实需要extern关键字来帮助区分未初始化的变量定义和变量前向声明(它们在其他方面看起来相同):

1
2
3
4
5
6
7
// 非常量
int g_x; // 变量定义 (可以不用显示初始化)
extern int g_x; // 前向声明 (无初始化)

// 常量
extern const int g_y { 1 }; // 变量定义 (const 外部变量定义需要初始化值)
extern const int g_y; // 前向声明 (无初始化)

快速摘要

1
2
3
4
5
6
7
8
9
// 外部全局变量定义:
int g_x;                       // 定义未初始化的外部全局变量 (默认初始化为0)
extern const int g_x{ 1 };     // 定义初始化了的 const 外部 全局变量 
extern constexpr int g_x{ 2 }; // 定义初始化了的 constexpr 外部 全局变量

// 前向声明
extern int g_y;                // 前向声明 非常量 全局变量
extern const int g_y;          // 前向声明 const 全局变量
extern constexpr int g_y;      // 不被允许: constexpr 变量 不能被前向声明

7.5 内部链接

上一节

7.7 为什么(非常量)全局变量是邪恶的

下一节