文章

extren关键字

extern声明外部变量或函数,告诉编译器在其他文件定义,支持跨文件访问。

extren关键字

extern 关键字

在 C++ 中,extern 是一个关键字,用来 声明 一个变量或函数是在其他文件(或作用域)中定义的,从而允许在多个文件中共享变量或函数定义。extern 主要用于 跨文件访问变量或函数,是实现多文件模块化编程的常用手段。

详细用法解析

用于变量

1
2
3
4
5
6
7
8
9
10
11
// file1.cpp
int globalVar = 42;  // 定义变量

// file2.cpp
extern int globalVar;  // 声明变量,告诉编译器它在别处定义

#include <iostream>
int main() {
    std::cout << globalVar << std::endl;
    return 0;
}

编译方法(用 g++):

1
g++ file1.cpp file2.cpp -o program

链接器会在链接阶段把 file1.cpp 中的定义和 file2.cpp 的引用匹配起来。

虽然 file2.cpp 可以不用头文件,但通常建议用头文件统一声明,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// globals.h
extern int globalVar;

// file1.cpp
#include "globals.h"
int globalVar = 42;  // 定义

// file2.cpp
#include "globals.h"
#include <iostream>

int main() {
    std::cout << globalVar << std::endl;
    return 0;
}
  • 避免在多个源文件中重复写 extern int globalVar;
  • 如果变量类型或名字改了,只需改头文件,保持一致性

用于函数

函数默认具有外部链接属性,因此即使不写 extern,也能跨文件使用。但加上 extern 更显式。

file1.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
// file1.cpp
void sayHello() {
    std::cout << "Hello from file1!" << std::endl;
}

// file2.cpp
#include <iostream>
extern void sayHello();  // 声明函数

int main() {
    sayHello();
    return 0;
}

虽然可以直接声明 extern,但推荐通过头文件统一声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// hello.h
#pragma once
#include <iostream>
void sayHello();

// file1.cpp
#include "hello.h"

void sayHello() {
    std::cout << "Hello from file1!" << std::endl;
}

// file2.cpp
#include "hello.h"

int main() {
    sayHello();
    return 0;
}
  • 避免在多个源文件中重复写函数声明
  • 保证函数签名一致,防止类型不匹配
  • 易于维护:修改函数签名只需改头文件

调用 C 语言函数

C++ 和 C 的名字修饰不同

在 C++ 中,函数名会被编译器修饰(mangle),以支持函数重载。

1
2
void foo(int);
void foo(double);
  • C++ 会把这两个函数的名字改为内部符号(伪代码):
1
2
_Z3fooi        // foo(int)
_Z3food        // foo(double)

但在 C 语言中,不支持函数重载,函数名直接就是原始名字,:

1
void foo();  // 编译后就是符号 foo
C++ 想调用一个用 C 写的函数怎么办?

如果在 C++ 中写:

1
void c_function();  // 默认是 C++ 链接方式,编译器可能找不到符号

但是实际这个函数是 C 写的、编译出来的符号叫 c_function(没有被修饰),C++ 链接器就找不到它,会报错:

1
undefined reference to `c_function`
正确做法

extern "C",禁用 C++ 的 name mangling:

1
extern "C" void c_function(); // 告诉编译器用 C 的方式来链接
举个实际例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// c_code.c
#include <stdio.h>

void say_hello() {
    printf("Hello from C!\n");
}

// main.cpp
extern "C" void say_hello();  // 告诉编译器:这是 C 函数

int main() {
    say_hello();
    return 0;
}

编译:

1
2
gcc -c c_code.c
g++ main.cpp c_code.o -o program
extern “C” 块状写法
1
2
3
4
5
6
7
8
9
10
#ifdef __cplusplus
extern "C" {
#endif

void c_function1();
void c_function2();

#ifdef __cplusplus
}
#endif

这样写可以让头文件在 C 和 C++ 中都能兼容使用

写法含义
extern "C"用 C 的方式链接(关闭 C++ 的函数名修饰)
extern "C" { ... }一组函数都按 C 的方式链接
extern "C" + C 头文件C++ 调用 C 编写的库的标准做法

与 static 的对比

关键词作用域链接类型是否跨文件共享
extern全局或局部外部链接可以
static文件内部或函数内部链接不可以

注意事项

extern 是声明,不是定义

1
2
extern int x; // 声明,不分配内存
int x = 10;   // 定义,分配内存

如果没有定义就使用,会链接错误(Linker Error)

1
2
3
4
extern int x;
int main() {
    std::cout << x; // 如果没有定义 x,会链接失败
}
写法声明(Declaration)定义(Definition)内存分配说明
int x;隐含声明是定义分配内存(全局/BSS 或栈)未初始化的全局变量自动为 0,局部变量值未定义
int x = 42;隐含声明是定义分配内存并初始化全局变量放在已初始化数据段,局部变量放在栈上
extern int x;仅声明不是定义不分配内存告诉编译器变量定义在别的地方
extern int x = 42; (C++17+)声明定义分配内存并初始化同时声明和定义,允许在 C++17 及以后版本使用
本文由作者按照 CC BY 4.0 进行授权