跨文件协作的关键字extern

extern 是啥

简单来说,extern 就是 C/C++ 里一个关键字,用来声明变量或函数,告诉编译器 “这个东西存在,定义在别的地方”,它的核心作用就是声明但不定义,避免重复定义错误。

基本使用场景

1)共享全局变量

假设我们有两个文件,File1.cppFile2.cpp

File1.cpp 里,我们定义了一个全局变量:

// File1.cpp
int globalVar = 10;  // 定义全局变量

然后在 File2.cpp 里,我们想用这个变量,就需要用 extern 声明它:

// File2.cpp
extern int globalVar; // 声明
void func() { 
    cout << globalVar; // 使用声明过的全局变量
}

这样,File2.cpp 就能用 File1.cpp 里的 globalVar 变量啦。

2)声明外部函数

假设我们有一个头文件 utils.h,和两个源文件 utils.cppmain.cpp

utils.h 中,我们可以声明一个函数:

// utils.h
extern void printMsg(); // 声明函数(其实这里的 extern 可以省略,因为函数默认是 extern 类型)

utils.cpp 中定义这个函数:

// utils.cpp
#include 
void printMsg() {
    cout << "Hello from utils!" << endl;
}

main.cpp 中,我们就可以调用这个函数啦:

// main.cpp
#include "utils.h"
int main() { 
    printMsg(); // 调用其他文件定义的函数
    return 0;
}

3)头文件中的最佳实践

在头文件中用 extern 声明全局变量,在对应的源文件中定义。

举个例子:

// config.h
extern int MAX_SIZE; // 声明变量
// config.cpp
int MAX_SIZE = 100; // 定义变量(这里才会分配内存)

这样做的好处是避免直接在头文件中定义变量,防止多个文件包含头文件时导致链接错误。

extern "C":C++ 调用 C 库

C++ 编译器有个小癖好,它会对函数名进行修饰,就像给函数名戴上不同的帽子,以便区分它们的功能和参数。这个过程通常是这样的:假设你写了一个函数 void myFunction(int),C++ 编译器可能会把它变成 _Z11myFunctioni 这样的名字(不同的编译器可能有不同的修饰规则,就像是不同的帽子款式)。但 C 编译器没有这个癖好,它会把函数名原封不动地保留。

现在,如果你有个用 C 语言编写的库文件,里面有个函数 void cFunction(),C 编译器处理后,这个函数名就是简单的 cFunction。但如果你在 C++ 代码中直接写 cFunction(),C++ 编译器会按照自己的规则给它加个 “帽子”,变成 _Z10cFunctionv 这样的名字。到了链接阶段,链接器会找不到原来的 cFunction,因为它只会看到 _Z10cFunctionv,从而报错。

这时候,extern "C" 就像是一把 “钥匙”,它告诉 C++ 编译器:“这个函数是 C 风格的,别给它加奇怪的帽子啦!” 举个例子:

 /*
 __cplusplus宏解释:
    当使用 C++ 编译器时,__cplusplus 宏被预定义,extern "C" 生效,确保函数按 C 规则编译。
    当使用 C 编译器时,忽略 extern "C"(因其不支持该语法),直接声明函数,避免语法错误。
 */
 #ifdef __cplusplus
 extern "C" {  // 使告诉编译器按 C 方式处理
 #endif
     void c_function(); // 声明 C 语言函数
 #ifdef __cplusplus
 }
 #endif

这样就能确保 C++ 编译器正确链接 C 语言函数啦。

注意事项

  • extern 变量必须在使用前声明,不然编译器会不认识这个变量。
  • 变量只能定义一次,但可以声明多次。定义是分配内存的操作,而声明只是告诉编译器变量存在。
  • 不要初始化 extern 变量,比如 extern int a = 5; 这样写是错误的,因为 extern 变量的初始化应该在定义它的文件中进行。

总之,掌握 extern 的用法,可以让代码在模块化开发中实现高效的协作,特别是在跨文件共享变量和函数时非常有用。

你可能感兴趣的:(C/C++,c++,开发语言,c语言)