当前位置: 首页 > news >正文

C语言extern的用法(非常详细,通俗易懂)

以往我们都是将所有的代码写到一个源文件里面,对于小程序,代码不过几百行,这或许无可厚非,但当程序膨胀代码到几千行甚至上万行后,就应该考虑将代码分散到多个文件中,否则代码的阅读和维护将成为一件痛苦的事情。

本节我们来演示一下多文件编程。在下面的例子中,我们创建了两个源文件 main.c 和 module.c:

  • module.c 是整个程序的一个模块,我们在其中定义了一个全局变量和一个函数;
  • main.c 是程序的主模块(主文件),它使用到了 module.c 中的变量和函数。

module.c 源码:

/**
*  系统学习C语言 https://xiecoding.cn/c/
**/
#include <stdio.h>int m = 100;void func(){printf("Multiple file programming!\n");
}

main.c 源码:

/**
*  系统学习C语言 https://xiecoding.cn/c/
**/
#include <stdio.h>extern void func();
extern int m;int n = 200;int main(){func();printf("m = %d, n = %d\n", m, n);return 0;
}

在 Visual Studio 中,将两个源文件都添加到工程中,点击“运行(Run)”按钮就可以运行程序。

在 Linux GCC 中,可以使用下面的命令来编译和运行程序:

$gcc main.c module.c
$./a.out

程序最终的运行结果为:

Multiple file programming!
m = 100, n = 200

m 和 n 是在所有函数之外定义的全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的代码文件,包括.c.h文件。

如果你一直在编写单个源文件的程序,那么请注意,全局变量的作用范围不是从变量定义处到该文件结束,在其他文件中也有效。

这里需要重点理解的是 extern 关键字,它用来声明一个变量或函数。

C语言extern关键字

我们知道,C语言代码是由上到下依次执行的,不管是变量还是函数,原则上都要先定义再使用,否则就会报错。但在实际开发中,经常会在函数或变量定义之前就使用它们,这个时候就需要提前声明。

所谓声明(Declaration),就是告诉编译器我要使用这个变量或函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上。

例如,我们知道使用 printf()、puts()、scanf()、getchar() 等函数要引入 stdio.h 这个头文件,很多初学者认为 stdio.h 中包含了函数定义(也就是函数体),只要有了头文件程序就能运行。其实不然,头文件中包含的都是函数声明,而不是函数定义,函数定义都在系统库中,只有头文件没有系统库在链接时就会报错,程序根本不能运行。

1) 函数的声明

函数的定义有函数体,函数的声明没有函数体,编译器很容易区分定义和声明,所以对于函数声明来说,有没有 extern 都是一样的。

总结起来,函数声明有四种形式:

//不使用 extern
datatype function( datatype1 name1, datatype2 name2, ... );
datatype function( datatype1, datatype2, ... );
//使用 extern
extern datatype function( datatype1 name1, datatype2 name2, ... );
extern datatype function( datatype1, datatype2, ... );

 

2) 变量的声明

变量和函数不同,编译器只能根据 extern 来区分,有 extern 才是声明,没有 extern 就是定义。

变量的定义有两种形式,你可以在定义的同时初始化,也可以不初始化:

datatype name = value;
datatype name; 

而变量的声明只有一种形式,就是使用 extern 关键字:

extern datatype name;

另外,变量也可以在声明的同时初始化,格式为:

extern datatype name = value;

这种似是而非的方式是不被推荐的,有的编译器也会给出警告,我们不再深入讨论,也建议各位读者把定义和声明分开,尽量不要这样写。

extern 是“外部”的意思,很多教材讲到,extern 用来声明一个外部(其他文件中)的变量或函数,也就是说,变量或函数的定义在其他文件中。

不过我认为这样讲不妥,因为除了定义在外部,定义在当前文件中也是正确的。例如,将 module.c 中的int m = 100;移动到 main.c 中的任意位置都是可以的。所以我认为,extern 是用来声明的,不管具体的定义是在当前文件内部还是外部,都是正确的。

http://www.lryc.cn/news/583156.html

相关文章:

  • 〔从零搭建〕数据湖平台部署指南
  • 17.Spring Boot的Bean详解(新手版)
  • OpenCV颜色矩哈希算法------cv::img_hash::ColorMomentHash
  • STM32-待机唤醒实验
  • [Leetcode] 预处理 | 多叉树bfs | 格雷编码 | static_cast | 矩阵对角线
  • User手机上如何抓取界面的布局uiautomatorviewer
  • 【机器人】Aether 多任务世界模型 | 4D动态重建 | 视频预测 | 视觉规划
  • 速卖通跨境运营破局:亚矩阵云手机如何用“本地化黑科技”撬动俄罗斯市场25%客单价增长
  • React 编译器与性能优化:告别手动 Memoization
  • 开始读 PostgreSQL 16 Administration Cookbook
  • 苍穹外卖项目日记(day04)
  • 【Netty+WebSocket详解】WebSocket全双工通信与Netty的高效结合与实战
  • 冷冻电镜重构的GPU加速破局:从Relion到CryoSPARC的并行重构算法
  • 《重构项目》基于Apollo架构设计的项目重构方案(多种地图、多阶段、多任务、状态机管理)
  • 仓颉语言 1.0.0 升级指南:工具链适配、collection 操作重构与 Map 遍历删除避坑
  • IT系统安全刚需:绝缘故障定位系统
  • Tailwind CSS纵向滚动条设置
  • PiscTrace深蹲计数功能实现:基于 YOLO-Pose 和人体关键点分析
  • 基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(4)集成Allure报表
  • JavaScript数组方法——梳理和考点
  • SpringBoot实现动态Job实战
  • DRT-Net: Dual-Branch Rectangular Transformer with Contrastive Learning
  • 离线二维码生成器,无需网络快速制作
  • springBoot使用XWPFDocument 和 LoopRowTableRenderPolicy 两种方式填充数据到word模版中
  • Android-重学kotlin(协程源码第一阶段)新学习总结
  • npm init vue@latestnpm error code ETIMEDOUT
  • 网络请求与现实生活:用办理业务类比理解HTTP通信
  • kotlin学习,val使用get()的问题
  • AI 知识库 2.0 时代!2025 设备管理系统如何实现 “智慧运维”?
  • Docker完全指南:从入门到生产环境实战