与C/C++关键字extern有关的原理
关键字有一定的语义,但是用法不唯一。
对于C/C++语言的预编译、编译、汇编、链接。我相信大家在接触C++一年不到就背的滚瓜烂熟,但是其中的细节,是后来才慢慢想明白的。为什么我不讲extern关键字呢?extern关键字的渊源深着呢,耐心学完前面的内容,extern的神秘面纱自然就解开了。
众所周知,C语言的出现先于C++,而汇编语言的出现又先于C。但是不管你用它们中任何一门语言编写程序。编译后都生成一个可执行的程序(前提是代码没有语法错误)。
对于使用汇编写好的程序,我们只需要把汇编源代码交给汇编器,汇编器就输出可执行文件给我们。
对于C语言写好的程序,我们把C源代码交给预处理器,预处理器把C源代码中的宏“消化”掉,生成纯C源代码,然后把纯C代码交给编译器,编译器输出相应的汇编代码,之后的处理方式就跟汇编写的一样了,交给汇编器,汇编代码与最后的可执行代码可以说是一一对应的。使用C比使用汇编方便,因为编译器为我们做了很多固定的事情,但是前提是你必须明白编译器的原理,不然就乱套了。
随着软件规模的发展,后来的人们发现 有的软件功能太多了,全部放在一个main.c文件中真的太大了,逻辑混乱,不好读。另外还有更重要的一点,在A程序中写过的一个功能,在B程序中又要使用同样的功能,需要去A的源代码里面找到,复制过来,复制错了还麻烦。所以出现了一个重要的概念,“多文件编译”:每一个.c文件经过一个编译流程都能生成一个目标文件。即使.c文件没有main函数,也能生成一个目标文件。这里我们约定,没有main函数的.c文件编译出来的叫做目标文件,含有main函数的.c文件编译出来的叫做待链接可执行文件。一个目标文件可以调用其他任何一个目标文件中定义的函数代码、变量,前提是另外一个目标文件“允许”,如何允许或者拒绝呢?这里就靠我们使用extern和static、auto这类关键词控制了,如何控制我们之后再细谈。
链接器就是这个时候产生的,一个待链接可执行文件+n个相关的目标文件=可执行文件,这里的n可以等于0。这样就形成了多文件编译,虽然过程复杂了点,但是把一个main.c分割成多个源文件,解决了上面说到的两个严重问题。当然你不喜欢链接也可以不链接,把所有内容写在main.c里就不需要链接过程了。我们想要编写应用程序时,第一件事就是创建一个.c文件,在里面写一个main函数,然后开始编写main函数。我们在main函数中可以调用一些其他的函数,这些函数可以不跟main函数在一个源文件里,注意,不在同一个源文件也就意味着不在同一个目标文件里了,不在同一个目标文件,就需要跨目标文件调用,这是链接器的工作,我们只需要把main函数可能用到的所有目标文件丢给链接器,链接器会根据函数名和调用关系把他们链接生成一个完整的可执行文件。所有的目标文件都可以互相调用,但是我们在编写程序时,大多数这个调用关系是单向的,最后汇总到main函数。如果一个函数(不管在哪个文件里)没有被main函数直接或者间接的使用,那他就没有被链接的必要,链接器在链接的时候就不把他链接进可执行文件里。
多文件编译 全靠 链接器 支持,链接器怎么这么神奇?它为什么知道要怎么链接?哈哈,其实链接器并没有你想的那么神奇,它并不是凭空就把目标文件链接起来了。在它链接之前,我们需要给它留下一些记号、给它一些提示。要是提示的不明显、有二义性,它就立即报错让你链接失败,这一点想必经历过多文件编译的人都深有体会。有语法错误编译器会提示我语法错误在第几行。但是有时明明没有语法错误,链接器还给我报满满一屏幕的编译错误,而且还不告诉我在第几行,这个就是链接错误。链接错误很简单,所有的链接错误都是链接器在说:根据你给我留下的提示,我找不到你想要调用的函数在哪,或者我找到了好多个可行选择,不知道选哪个。。。
接下来重点来了,我告诉你如何提示链接器正确的链接。接下来我会用到3个关键词extern,static,auto。
纯C语言源代码由全局函数和全局变量组成(局部变量总是包含在全局函数里,算是全局函数的子集,就不作数了),对于函数一共有这3种写法:int fun(); static int fun(); extern int fun()。对于变量一共有4种写法:int val; auto int val; static int val; extern int val;
额,为什么不对称呢,函数为什么不能auto int fun();呢?
接下来我说几个C链接器的游戏规则:
写int fun();和写extern int fun();是一样的意思。意思是:这个函数可以让其他目标文件调用。而static int fun();的意思相反,不让其他目标文件看到这个函数,让他们在链接时找不到,只有在本文件内才能调用static修饰的函数。对于函数,就这么两个规则。
对于全局变量,写int val;和auto int val;意思一样,都是让其他源文件可以访问。而写static int val;意思就是只能在本文件内部使用。写extern int val;的意思就是:val变量不是在这里产生的,而是其他文件定义的,我想调用它,事先声明一下,这就叫做扩展声明。而函数就不需要扩展声明,因为没有函数体的函数都是函数声明,所以不需要留有关键词做提示。
现在大概明白这三个关键词的语义了吧。static最直当明了,沾到它别的文件就别想访问了。而auto几乎没什么用,几乎没人会在这里用auto(c++11中auto关键词可以用来让编译器自动推导出类型,使代码简洁)。
再后来,有了C++这门语言,编译器为我们做了更多的事情,比C编译器做的还多。最简单的,重载函数,为了支持重载函数,编译器会在编译时为它们重命名。这时如果你不知道C++的游戏规则,可能又会使链接器找不到链接对象了。
与C/C++关键字extern有关的原理的更多相关文章
- C# 关键字extern用法
修饰符用于声明在外部实现的方法.extern 修饰符的常见用法是在使用 Interop 服务调入非 托管代码时与 DllImport 属性一起使用:在这种情况下,该方法还必须声明为 static,如下 ...
- 【转载】理解C语言中的关键字extern
原文:理解C语言中的关键字extern 最近写了一段C程序,编译时出现变量重复定义的错误,自己查看没发现错误.使用Google发现,自己对extern理解不透彻,我搜到了这篇文章,写得不错.我拙劣的翻 ...
- volatile关键字的作用、原理
在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到"半个"单例. 而发挥神奇作用的volatile,可以当之 ...
- 【校招面试 之 C/C++】第29题 C/C++ 关键字extern
1.extern "C" extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示 ...
- 面试题:volatile关键字的作用、原理
在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到“半个”单例. 而发挥神奇作用的volatile,可以当之无愧的被称为Java ...
- C/C++关键字 extern
1.基本解释:extern 可置于变量或函数前面,表示变量或函数的定义在别的文件中,以提示编译器遇到此变量或函数时在其他模块中寻找定义. extern还有另外2个作用.第一:与“C”连用时,如 ext ...
- 关键字 extern
定义:extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中.编译器会到其他模块中寻找其定义. extern int f(); extern int i; extern关键字 作为 ...
- 18.21 关键字extern
用#include可以包含其他头文件中变量.函数的声明,为什么还要extern关键字? 1.头文件 其实头文件对计算机而言没什么作用,只是在预编译时在#include的地方展开一下,没别的意义了.将头 ...
- Java的 volatile关键字的底层实现原理
我们知道volatile关键字的作用是保证变量在多线程之间的可见性,它是java.util.concurrent包的核心,没有volatile就没有这么多的并发类给我们使用.本文详细解读一下volat ...
随机推荐
- Java(1)JDK安装
1.安装JDK开发环境 下载网站:http://www.oracle.com/ 开始安装JDK: 修改安装目录如下: 确定之后,单击"下一步". 注:当提示安装JRE时,可以选择不 ...
- 大项目小细节---onbeforeunload增强用户体验
微信公众平台编辑数据页面,点击浏览器回退按钮.刷新按钮(包括F5.Ctrl+R).关闭页面.点击其他超链接等操作的时候,会提示弹窗提示. 为增加用户体验,我们也增加类似功能. 代码如下: @if (R ...
- JxBrowser之一:Java嵌入Chrome浏览器
1.JxBrowser是一个让Java可以轻松集成Chrome浏览器的跨平台库:一般用于网络爬虫或者网页自动化测试等. 官方网址:https://jxbrowser.support.teamdev.c ...
- Mac+Apache+PHP 安装 Xdebug 方法
MAC homebrew自2018/3/31之后弃用homebrew/php By 31st March we will deprecate and archive the Homebrew/php ...
- Django框架简介-视图系统
2.3 视图系统 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档 ...
- vue实现tab切换功能
最近用vue做一个页面的tab功能,经过一查找资料,没用路由,也没用动态组件,完美实现了tab切换功能,效果如下 下面是代码实现,这是模板 <article id="example&q ...
- python -- 内置模块02
1.os 所有和操作系统相关的内容都在os模块,一般用来操作文件系统 import os os.makedirs('dirname1/dirname2') # 可生成多层递归目录 os.removed ...
- less和sass的定义和区别
less是单独的一种文件,可以理解为css的升级版: sass是一个相对新的编程语言,为web前端开发而生,可以用它来定义一套新的语法规则和函数: 区别: ① 表现方式不同:less基于javas ...
- Python中文问题
读取数据库中文是?? 解决如下 一.python2版本需要在 文件的开头要加上编码设置来说明文件的编码 python3版本以上不需要 #encoding=utf-8 二.在连接数据的连接参数里加上字 ...
- 超简单的实现wordcount
worcount1.0,源码参见GitHub:https://github.com/18382271904/spring_lee_flag.git