C语言extern关键字作用分析
本文主要介绍C语言中extern的作用分析
前提
首先理解声明和定义:
声明向编译器说明标识符及其类型;定义也是声明,并为对象提供存储或给出函数体。
提示
变量的声明和定义不一定同时发生。extern int x; 通常只是声明;在文件作用域,带初始化器的 extern int x = 1; 则是定义。文件作用域的 int x; 是暂定定义。
extern作用
在C语言中,extern 是存储类别说明符,参与确定标识符的链接属性。常见用法是不带初始化器地声明变量,供当前文件引用。具体来说,extern的作用有以下几点:
变量声明:当
extern用于不带初始化器的变量声明时,这个声明本身不为变量提供存储。变量的定义可以在同一个翻译单元,也可以在另一个翻译单元;声明并不表示定义已经出现。// 在一个文件中声明变量 extern int x;函数声明:函数声明也可以显式使用
extern,但通常可以省略。它不表示函数一定在其他文件中定义。// 声明函数的存在 extern void someFunction();全局变量:文件作用域的
extern声明使标识符从声明处到文件末尾可见;块作用域的声明则只在相应块内可见。多个翻译单元通过外部链接引用同一对象,但每个使用它的文件仍需看到相应声明。// 在一个文件中使用 extern 声明全局变量 extern int globalVariable;
共享对象通常在一个源文件中定义,再把声明放进头文件供其他文件包含。extern并不总是建立外部链接:如果可见的先前声明已经指定内部链接,后续的 extern 声明会延续该链接。声明、定义、作用域和链接属性需要分别判断。
案例: 使用extern模拟面向对象编程
下面用结构体保存数据和函数指针,模拟对象的组织方式;extern用于声明由另一个源文件定义的对象。对象式组织来自结构体与函数指针,extern本身不提供这些能力。
首先,我们创建一个头文件myclass.h,其中定义了结构体类型MyClass,并声明共享对象及相关函数:
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
typedef struct {
int data;
void (*printData)();
} MyClass;
extern MyClass myObject;
void MyClass_init();
void MyClass_printData();
#endif然后,我们创建一个源文件myclass.c,实现了类的初始化和打印数据的方法:
// myclass.c
#include <stdio.h>
#include "myclass.h"
MyClass myObject;
void MyClass_init() {
myObject.data = 2023;
myObject.printData = MyClass_printData;
}
void MyClass_printData() {
printf("Data: %d\n", myObject.data);
}接下来,我们创建一个主文件main.c,在其中使用类和对象:
// main.c
#include "myclass.h"
extern MyClass myObject;
int main() {
MyClass_init();
myObject.printData();
return 0;
}运行结果

在这个例子中,我们使用extern关键字在main.c中引用了在myclass.c中定义的myObject对象。通过调用MyClass_init()方法初始化对象,并通过myObject.printData()调用对象的方法。头文件已经声明了共享对象,因此 main.c 中重复的声明可以省略。
编译这些文件并运行程序,你将看到输出结果为Data: 2023,并换行。这说明借助extern声明,两个源文件访问的是同一个共享对象。
这是一种用 C 组织对象和方法的模式;extern只处理声明及链接关系,不提供封装、继承或动态分派。示例沿用了旧笔记的空参数列表写法:在 C17 及以前,头文件中不带函数体的这类声明不指定参数信息;明确无参数时,应在参数列表中写 void。
参考链接:
C11 公开草案 N1570:§6.2.1、§6.2.2、§6.7.6.3、§6.9.2。
Difference between Definition and Declaration - GeeksforGeeks