extern在C语言中到底怎么用?

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

extern 是 C 语言中一个非常重要但常常被误解的关键字,它的核心作用是声明,而不是定义

externa C语言
(图片来源网络,侵删)

extern 的核心概念:声明 vs. 定义

在深入 extern 之前,必须彻底理解 C 语言中“声明”和“定义”的区别:

  • 定义:一个定义会为变量或函数分配内存空间,它告诉编译器“这个东西是什么,以及它在哪里”。

    • 对于变量:int a = 10; 既是声明,也是定义,它分配了内存,并初始化了值。
    • 对于函数:void myFunction() { ... } 是一个定义,它提供了函数的实际代码体,编译器会为这些指令分配内存。
  • 声明:一个声明只是告诉编译器这个东西的存在和类型,但它不分配内存空间,它相当于说:“嘿,编译器,别担心,这个名字(比如变量 a 或函数 myFunction)会在别的地方被定义,你现在只需要知道它的类型就行了。”

    • 对于变量:extern int a; 是一个声明,它告诉编译器 a 是一个整型变量,但它的内存分配和初始化在别处。
    • 对于函数:void myFunction(); 是一个声明(函数原型),它告诉编译器 myFunction 是一个返回 void 的函数,不接受参数,但它的具体实现(定义)在别处。

extern 的唯一作用就是进行“声明”,并且是“外部链接”的声明。

externa C语言
(图片来源网络,侵删)

extern 的主要用途

extern 主要用于解决在多个源文件(.c 文件)之间共享变量和函数的问题。

跨文件共享全局变量

这是 extern 最经典和最常见的用法。

场景:假设你有一个项目,由两个文件组成:main.cutils.c

utils.c (定义文件)

externa C语言
(图片来源网络,侵删)
// 这是一个定义,为变量 counter 分配内存,并初始化。
int counter = 0; 

main.c (使用文件)

#include <stdio.h>
// 这是一个声明,它告诉 main.c 文件:
// "counter" 这个变量是一个整型,它已经被定义在别处了。
// 编译器看到这个声明,就不会在 main.c 中为 counter 重新分配内存。
extern int counter;
void increment_counter() {
    counter++; // 可以访问和修改这个全局变量
}
int main() {
    printf("Initial counter: %d\n", counter); // 输出 0
    increment_counter();
    printf("After increment: %d\n", counter); // 输出 1
    return 0;
}

编译和链接过程

  1. 编译:编译器分别编译 utils.cmain.c
    • utils.c 中,编译器看到了 int counter = 0;,知道这是一个定义,并为其分配内存。
    • main.c 中,编译器看到了 extern int counter;,知道 counter 是一个外部链接的整型变量,但它的大小和类型信息已经记录,它不会为 countermain.c 中分配内存。
  2. 链接:链接器将所有编译后的目标文件(.o 文件)组合在一起,当链接器在 main.c 的目标文件中找不到 counter 的实际内存地址时,它会去 utils.c 的目标文件中寻找,找到后,它会将所有对 counter 的引用都指向 utils.c 中分配的那个地址。

如果没有 extern 会怎样? 如果在 main.c 中直接使用 int counter;main.c 也会把它当作一个定义,为 counter 分配另一块独立的内存,这样,main.c 中的 counterutils.c 中的 counter 就是两个完全不同的变量,修改一个不会影响另一个,这通常不是我们想要的结果。

函数声明(隐式 extern

对于函数,情况稍微特殊一些,在 C 语言中,函数默认具有外部链接

这意味着,如果你在一个文件中定义了一个函数,你可以在其他文件中直接使用它,而不需要显式地使用 extern 关键字

my_functions.c

// 函数定义
void print_message() {
    printf("Hello from my_functions.c!\n");
}

main.c

#include <stdio.h>
// 下面这两种声明方式是等价的
// void print_message(); // 标准的函数原型声明
// extern void print_message(); // 显式使用 extern 的声明
int main() {
    print_message(); // 可以直接调用,链接器会找到它的定义
    return 0;
}

虽然 extern 对函数声明是合法的,但通常不推荐使用,因为 extern void print_message(); 看起来比 void print_message(); 更冗余,后者是更通用的写法。


externstatic 的对立

externstatic 是两个完全相反的关键字,它们都控制着变量的链接属性

关键字 作用域 链接属性 生命周期
extern 文件作用域 外部链接 整个程序运行期间
static 文件作用域 内部链接 整个程序运行期间

static 的作用:当一个全局变量或函数被 static 修饰时,它的链接属性变为内部链接,这意味着它只能在定义它的源文件(.c 文件)内部被访问,其他文件无法看到它。

示例utils.c

// 这个变量只能在 utils.c 内部被访问
static int internal_var = 100;
// 这个函数也只能在 utils.c 内部被调用
static void helper_function() {
    printf("Helper function called.\n");
}

main.c

#include <stdio.h>
// extern int internal_var; // 错误!链接器会报错,找不到 internal_var
// extern void helper_function(); // 错误!链接器会报错,找不到 helper_function
int main() {
    // printf("%d\n", internal_var); // 编译错误,undeclared identifier
    // helper_function(); // 编译错误,undeclared identifier
    return 0;
}

static 就像是在一个文件内部建立了一道“墙”,将变量和函数限制在墙内,防止被其他文件误用或修改,这有助于封装和提高代码的模块化程度。


externconst 的结合

extern 也可以和 const 结合使用,来声明一个跨文件共享的只读常量

constants.h (头文件)

#ifndef CONSTANTS_H
#define CONSTANTS_H
// 声明一个外部的、只读的常量 PI
extern const double PI;
#endif

constants.c (定义文件)

#include "constants.h"
// 定义并初始化这个常量
// const 关键字确保它的值不会被修改
const double PI = 3.141592653589793;

main.c (使用文件)

#include <stdio.h>
#include "constants.h"
int main() {
    printf("The value of PI is: %f\n", PI);
    // PI = 3.14; // 编译错误!不能修改 const 变量
    return 0;
}

这种方式比在头文件中直接 #define PI 3.14159const double PI = 3.14159; 更优,后者会导致在每个包含头文件的 .c 文件中都创建一份独立的常量副本,而使用 extern const 可以确保常量在程序中只存在一份实例,节省内存。


总结表格

关键字组合 含义 示例 作用
int var; 定义一个具有外部链接的全局变量 int global_count = 0; 在当前文件中定义,并可被其他文件通过 extern 访问。
extern int var; 声明一个具有外部链接的全局变量 extern int global_count; 告诉编译器 var 在别处定义,不分配内存,用于跨文件访问。
static int var; 定义一个具有内部链接的全局变量 static int local_count = 0; 在当前文件中定义,但不能被其他文件访问。
void func(); 声明一个具有外部链接的函数(隐式 extern void my_func(); 告诉编译器函数原型,具体实现(定义)在别处。
extern void func(); 声明一个具有外部链接的函数(显式 extern extern void my_func(); void my_func(); 功能相同,但不常用。
extern const int var; 声明一个外部的、只读的常量 extern const int MAX_SIZE; 用于跨文件共享一个只读常量,确保内存中只有一份实例。

核心要点

  1. extern 的核心是声明,用于告诉编译器某个标识符(变量或函数)的类型和存在,但不分配内存
  2. 它的主要用途是实现跨文件共享,解决多文件编译项目中全局变量和函数的访问问题。
  3. externstatic 相反,extern 提供外部链接,static 限制为内部链接。
  4. 对于函数,extern 是隐含的,通常不需要显式写出。
-- 展开阅读全文 --
头像
C语言internal关键字有何作用与使用场景?
« 上一篇 今天
dede文章时间标签
下一篇 » 今天

相关文章

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

目录[+]