extern变量声明与定义的区别是什么?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

extern 是什么?

extern 是 C 语言中的一个存储类型说明符(Storage Class Specifier),它的核心作用是声明一个变量或函数,而不是定义它。

c语言 extern 变量
(图片来源网络,侵删)
  • 声明:告诉编译器“这个名字存在,它的类型是这个,但我现在不分配内存空间给你”,编译器会相信你,并期望在链接阶段找到这个名字的实际定义。
  • 定义:告诉编译器“这个名字存在,它的类型是这个,并且我现在要为你分配内存空间”,一个变量在一个程序中只能被定义一次。

extern 的作用是:“这个变量(或函数)是在别处定义的,我这里只是提前告诉编译器它的存在和类型。”


extern 变量的主要用途

extern 变量主要用于解决在多个源文件(.c 文件)之间共享全局变量的问题,在一个大型项目中,我们通常会将代码拆分成多个文件,extern 就是实现这种跨文件通信的关键。


单个文件中的 extern

我们先来看一个简单的例子,理解 extern 在单个文件中的作用。

场景:函数中使用全局变量

假设你有一个全局变量,但在它被定义之前,就想在某个函数里使用它。

c语言 extern 变量
(图片来源网络,侵删)
// file: main.c
#include <stdio.h>
// 函数声明
void printGlobalVar();
int main() {
    printGlobalVar(); // 在 globalVar 定义之前调用函数
    return 0;
}
// 全局变量的定义
int globalVar = 100;
// 函数定义
void printGlobalVar() {
    // 如果这里直接使用 globalVar,编译器会报错,
    // 因为编译器从上到下扫描,还不知道 globalVar 是什么。
    // 所以需要 extern 声明。
    extern int globalVar; // 告诉编译器 globalVar 是一个 int 类型,在别处定义了
    printf("The value of globalVar is: %d\n", globalVar);
}

解释:

  • int globalVar = 100;定义,它为 globalVar 分配了内存空间,并初始化为 100。
  • extern int globalVar;声明,它只是告诉编译器“存在一个名为 globalVarint 变量”,它不会分配新的内存空间。
  • printGlobalVar 函数中,由于函数定义在 globalVar 定义之后,编译器在编译 printGlobalVar 时已经“知道”了 globalVar,这里的 extern 声明在技术上不是必需的,但它是一个很好的编程习惯,可以明确表达意图。

多个文件中的 extern(核心用法)

这是 extern 最常见和最重要的用法,假设我们有两个文件:main.cutils.c

场景:在 main.c 中使用 utils.c 中定义的全局变量

步骤 1:在 utils.c 中定义全局变量

这个文件是变量的“家”,这里是变量的实际定义。

// file: utils.c
#include <stdio.h>
// 全局变量的定义
// 注意:这里没有 extern
int sharedCounter = 0;
void incrementCounter() {
    sharedCounter++;
    printf("Counter incremented in utils.c. New value: %d\n", sharedCounter);
}
  • 关键点int sharedCounter = 0; 是一个完整的定义,编译器会为它在内存中分配空间。

步骤 2:在 main.c 中使用 extern 声明该变量

这个文件需要使用 sharedCounter,但它自己不定义它。

// file: main.c
#include <stdio.h>
// 声明在 utils.c 中定义的全局变量
// extern 告诉编译器:sharedCounter 是一个 int,
// 它的真实定义在别处(链接时会找到)。
extern int sharedCounter;
// 声明在 utils.c 中定义的函数
void incrementCounter();
int main() {
    printf("Initial value of sharedCounter from main.c: %d\n", sharedCounter);
    incrementCounter(); // 调用 utils.c 中的函数
    incrementCounter(); // 再次调用
    printf("Final value of sharedCounter from main.c: %d\n", sharedCounter);
    return 0;
}
  • 关键点
    1. extern int sharedCounter; 是一个声明,不是定义,编译器看到这个声明后,就知道 sharedCounter 是一个整数,但不会在 main.c 中为它分配内存。
    2. 当编译器将 main.cutils.c 分别编译成目标文件(main.outils.o)后,链接器(Linker)会将它们链接在一起,链接器会看到 main.o 中有一个对 sharedCounter 的引用,然后在 utils.o 中找到它的实际定义,并将两者关联起来。

编译和运行: 你需要将两个文件一起编译:

gcc main.c utils.c -o my_program

运行结果:

Initial value of sharedCounter from main.c: 0
Counter incremented in utils.c. New value: 1
Counter incremented in utils.c. New value: 2
Final value of sharedCounter from main.c: 2

这个例子完美地展示了 extern 如何让不同文件共享同一个全局变量。


externconst 变量

externconst 一起使用时,事情会变得稍微复杂一些,因为 const 变量的默认链接属性是内部链接(internal linkage),而普通全局变量是外部链接(external linkage)。

  • 内部链接:变量只在当前文件内可见。
  • 外部链接:变量可以在其他文件中通过 extern 使用。

为了让一个 const 变量可以被其他文件使用,你必须显式地用 extern 声明它。

示例:

file: constants.h

// 头文件中只放声明,不放定义,是良好实践
extern const double PI; 

file: constants.c

#include "constants.h"
// 定义 const 变量
// 必须初始化
const double PI = 3.14159;

file: main.c

#include <stdio.h>
#include "constants.h" // 包含声明
int main() {
    // 编译器知道 PI 是一个 const double,并且定义在 constants.c 中
    printf("The value of PI is: %f\n", PI);
    return 0;
}

如果不加 externconst double PI;constants.c 中只是一个内部链接的变量,main.c 将无法访问它。


externstatic 的对比

staticextern 在某种程度上是“对立”的,它们都影响变量的链接属性。

特性 extern static (用于全局变量)
作用 声明一个在其他地方定义的变量/函数 定义一个变量/函数
链接属性 外部链接 (External Linkage) 内部链接 (Internal Linkage)
可见范围 在整个程序的所有文件中可见(通过 extern 声明) 仅在定义它的源文件内可见
内存分配 不分配内存,链接时关联到实际定义处 在程序的全局数据区分配内存
示例 extern int x; static int y = 10;

  • extern 是“向外看”,让变量可以被外部文件访问。
  • static 是“向内锁”,将变量的作用域限制在当前文件内。

总结与最佳实践

  1. extern 用于声明:它告诉编译器一个变量或函数的存在和类型,但承诺其定义在别处。
  2. 核心用途:实现跨文件的全局变量共享,在一个文件中定义,在另一个或多个文件中通过 extern 声明来使用。
  3. 头文件的角色extern 声明会放在头文件(.h 文件)中,这样,任何需要使用这个全局变量的源文件(.c 文件)只需要包含这个头文件即可,非常方便。
    • 定义:放在一个 .c 文件中(globals.c)。
    • 声明:放在对应的 .h 文件中(globals.h)。
  4. 避免滥用全局变量:虽然 extern 很方便,但过多使用全局变量会增加代码的耦合度,使程序难以维护和调试,在可能的情况下,优先使用函数参数和返回值来传递数据。
  5. const 变量:要让 const 全局变量具有外部链接,必须在声明时使用 extern

希望这个详细的解释能帮助你完全理解 C 语言中的 extern 变量!

-- 展开阅读全文 --
头像
dede5.7手册核心问题是什么?
« 上一篇 今天
C语言string函数有哪些常用功能?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]