前面我们都是将所有的代码写到一个源文件里面,对于小程序,代码不过几百行,这或许无可厚非,但当程序膨胀代码到几千行甚至上万行后,就应该考虑将代码分散到多个文件中,否则代码的阅读和维护将成为一件痛苦的事情。
本节我们就来演示一下多文件编程。在下面的例子中,我们创建了两个源文件 main.c 和 module.c:
module.c 是整个程序的一个模块,我们在其中定义了一个全局变量和一个函数;
main.c 是程序的主模块(主文件),它使用到了 module.c 中的变量和函数。
module.c 源码:
#include <stdio.h>
int m = 100;
void func(){
printf("Multiple file programming!\n");
}
main.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 关键字,它用来声明一个变量或函数。
extern 关键字
我们知道,C语言代码是由上到下依次执行的,不管是变量还是函数,原则上都要先定义再使用,否则就会报错。但在实际开发中,经常会在函数或变量定义之前就使用它们,这个时候就需要提前声明。

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

例如,我们知道使用 printf()、puts()、scanf()、getchar() 等函数要引入 stdio.h 这个头文件,很多初学者认为 stdio.h 中包含了函数定义(也就是函数体),只要有了头文件程序就能运行。其实不然,头文件中包含的都是函数声明,而不是函数定义,函数定义都在系统库中,只有头文件没有系统库在链接时就会报错,程序根本不能运行。
1) 函数的声明
在《C语言函数声明以及函数原型》一节中我们讲到了函数声明,那时并没有使用 extern 关键字,这是因为,函数的定义有函数体,函数的声明没有函数体,编译器很容易区分定义和声明,所以对于函数声明来说,有没有 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 是用来声明的,不管具体的定义是在当前文件内部还是外部,都是正确的。

C语言:extern应用的更多相关文章

  1. C语言extern作用(全局变量)

    用C语言编写程序的时候,我们经常会遇到这样一种情况:希望在头文件中定义一个全局变量,然后包含到两个不同的c文件中,希望这个全局变量能在两个文件中共用. 举例说明:项目文件夹project下有main. ...

  2. C语言 extern学习2 分析

    上一篇文章中,通过头文件声明,而调用有一个特别大的漏洞: 为什么编译器可以链接过来呢,因为默认是extern修饰的,这种类似全局作用域的功能使其可以被调用 继续加强学习: 这一次有两对C文件: fir ...

  3. C语言extern关键字使用

    在chinaunix上看见一篇转载的文章,觉得特别好,关于extern使用的解释: 参考链接:http://doc.chinaunix.net/CPP/201206/2248432.shtml 在C语 ...

  4. C语言 extern学习1

    没有头文件时,通过本文件内的函数声明来确定定义域,实现功能: //单文件测试 #include <stdio.h> /* 经测试,C语言环境下子函数默认是void型:所以可省略不写 为严谨 ...

  5. 浅谈C语言 extern 指针与数组

    /* * d.c * * Created on: Nov 15, 2011 * Author: root */ #include "apue.h" int a[] = {3,2}; ...

  6. C语言学习及应用笔记之六:C语言extern关键字及其使用

    在C语言中,修饰符extern用在变量或者函数的声明前,用来以标识变量或者函数的定义在别的文件中,提示编译器遇到此变量或者函数时,在其它文件中寻找其定义.extern关键字的用法有几种,我们下面对其进 ...

  7. C++ Primer Plus读书笔记

    第五章 循环和关系表达式 1. 2.类别别名: (1)   #define FLOAT_POINTER float * FLOAT_POINTER pa, pb; 预处理器置换将该声明转换成  flo ...

  8. Linux下的动态连接库及其实现机制

    Linux与Windows的动态连接库概念相似,但是实现机制不同.它引入了GOT表和PLT表的概念,综合使用了多种重定位项,实现了"浮动代码",达到了更好的共享性能.本文对这些技术 ...

  9. RT-Thread--内核移植

    内核移植 内核移植就是指将 RT-Thread 内核在不同的芯片架构.不同的板卡上运行起来,能够具备线程管理和调度,内存管理,线程间同步和通信.定时器管理等功能.移植可分为 CPU 架构移植和 BSP ...

  10. 《OOC》笔记(1)——C语言const、static和extern的用法

    <OOC>笔记(1)——C语言const.static和extern的用法 C语言中const关键字用法不少,我只喜欢两种用法.一是用于修饰函数形参,二是用于修饰全局变量和局部变量. 用c ...

随机推荐

  1. python 获取时间范围内日期列表

    python 获取时间范围内日期列表 import datetime def dateRange(beginDate, endDate): dates = [] dt = datetime.datet ...

  2. Python+Selenium - 一个元素有两个标签时:一个用于显示,一个用于底层

    如下图,这种一个元素由两个标签控制的,用js改值时需要两个标签的属性值都改 改值代码 js_code = 'a = document.getElementById("lostdate&quo ...

  3. 201871030137-杨钦颖 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

    201871030137-杨钦颖 实验三 结对项目-<D{0-1}KP 实例数据集算法实验平台>项目报告 项目 内容 课程班级博客链接 班级连接 这个作业要求链接 作业连接 我的课程学习目 ...

  4. MSQL 数据完整性(约束)

    0. 前言 1. 实体完整性 1.1 主键约束(primary key) 1.2 唯一约束(unique) 1.3 自动增长列(auto_increment) 2. 域完整性 数据类型 非空约束 默认 ...

  5. TinyML-TVM如何驯服TinyML

    TinyML-TVM如何驯服TinyML 低成本,以人工智能为动力的消费类设备的激增,导致机器学习研究人员和从业人员对"裸机"(低功耗,通常没有操作系统)设备产生了广泛的兴趣.尽管 ...

  6. 使用TENSORRT和NVIDIA-DOCKER部署深部神经网络

    使用TENSORRT和NVIDIA-DOCKER部署深部神经网络 当前部署工作流

  7. springboot——简单通过Map将错误提示输出到页面显示

    主要思路:在controller层我们将错误信息put进map中,然后通过视图解析器跳转到目标页面,在目标页面中在通过指定标签内的th:text将错误消息取出. 例: 1.编写controller代码 ...

  8. 八、Nginx的TCP/UDP调度器

    nginx 1.9后才可以调用其他应用 1.9前只能调用web 部署nginx服务器----配置----起服务.验证 部署nginx服务器: [root@proxy ~]# yum –y instal ...

  9. cmd快捷键设置

    操作系统:windows7 x64 目的:像在任意空白处右键都有新建文档的功能一样,将cmd添加到右键里. 操作:百度经验 http://jingyan.baidu.com/article/948f5 ...

  10. 生成树协议(STP)

    一.交换网络环路的产生 1.广播风暴的形成 2.多帧复制 3.MAC地址表紊乱 二.STP简介 STP-Spanning Tree Protocol(生成树协议) 逻辑上断开环路,防止广播风暴的产生 ...