extern用法详解(转)_数组全局变量 extern-程序员宅基地

技术标签: C语言  

extern外部声明全局变量:

全局变量定义:int  a = 10;

则全局变量的外部声明只需指明extern、类型、变量名即可,不可在外部声明时再进行赋值,如可:extern int a;

全局数组定义:int array[10] = {12,3,1,4};

则全局数组的外部声明为:extern int array[ ];

常在两个地方用extern声明:

一是全局变量在源文件末尾定义,则可在源文件的前面进行extern声明,则从extern声明开始到末尾此全局变量都可有效;

二是全局变量在别的源文件中定义,则可在本源文件中进行extern声明,也可在一个头文件中extern 声明,然后本源文件包含此头文件,不管是在源文件中声明还是在头文件中声明,都需要在编译时加入定义此全局变量的源文件。

static修饰的全局变量:

则此全局变量就不能再被外部文件进行extern引用了,其作用范围就只能是本源文件。


extern外部声明函数:

在一个源文件中引用另一源文件,同外部变量一样可以在源文件中也可以在头文件中进行函数的声明,不同的是,对于外部变量的外部声明一定要加上extern ,而对于函数的外部声明则可以加extern也可以不加。

static修饰的函数:

用函数的定义的时候如果用了static进行修饰,则其它源文件就无法对此函数进行外部声明和引用了,此函数只能在此源文件中进行引用。

========================================================================================

源自:http://baike.baidu.com/view/963932.htm

在一个 源文件里定义了一个 数组:char a[6];
在另外一个文件里用下列语句进行了声明:extern char *a;
请问,这样可以吗?
答案与分析:
1)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的 指针并不等价于类型T的 数组。extern char *a声明的是一个 指针变量而不是字符 数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]。
2)、例子分析如下,如果a[] = "abcd",则外部变量a=0x12345678 ( 数组的起始地址),而*a是重新定义了一个 指针变量,a的地址可能是0x87654321,直接使用*a是错误的.
3)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。
4)、extern用在 变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明。
这个 关键字真的比较可恶,在定义( 函数)的时候,这个extern居然可以被省略,所以会让你搞不清楚到底是声明还是定义,下面分变量和函数两类来说:
尤其是对于变量来说。
extern int a;//声明一个全局变量a
int a; //定义一个全局变量a
extern int a =0 ;//定义一个全局变量a 并给初值。一旦给予赋值,一定是定义,定义才会分配存储空间。
int a =0;//定义一个全局变量a,并给初值,
声明之后你不能直接使用这个变量,需要定义之后才能使用。
第四个等于第三个,都是定义一个可以被外部使用的全局变量,并给初值。
糊涂了吧,他们看上去可真像。但是 定义只能出现在一处。也就是说,不管是int a;还是int a=0;都只能出现一次,而那个 extern int a可以出现很多次
当你要引用一个全局变量的时候,你就要声明extern int a;这时候extern不能省略,因为省略了,就变成int a;这是一个定义,不是声明。

2编译、链接编辑

声明外部变量
现代 编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的 全局变量是互相不透明的。也就是说,在编译时, 全局变量的可见域限制在文件内部。
下面举一个简单的例子:
创建一个工程,里面含有A.cpp和B.cpp两个简单的C++源文件:
1
2
3
4
5
6
7
//A.cpp
inti;
intmain()
{
}
//B.cpp
inti;
这两个文件极为简单,在A.cpp中我们定义了一个 全局变量i,在B中我们也定义了一个 全局变量i。
我们对A和B分别编译,都可以正常通过编译,但是进行链接的时候,却出现了错误,错误提示如下:
1
2
3
4
5
Linking...
B.obj:errorLNK2005:"inti"(?i@@3HA)alreadydefinedinA.obj
Debug/A.exe:fatalerrorLNK1169:oneormoremultiplydefinedsymbolsfound
Errorexecutinglink.exe.
A.exe-2error(s),0warning(s)
这就是说,在编译阶段,各个文件中定义的 全局变量相互是不透明的,编译A时觉察不到B中也定义了i,同样,编译B时觉察不到A中也定义了i。
但是到了链接阶段,要将各个文件的内容“合为一体”,因此,如果某些文件中定义的 全局变量名相同的话,在这个时候就会出现错误,也就是上面提示的重复定义的错误。
因此,各个文件中定义的 全局变量名不可相同。
在链接阶段,各个文件的内容(实际是编译产生的obj文件)是被合并到一起的,因而,定义于某文件内的 全局变量,在链接完成后,它的可见范围被扩大到了整个程序。
这样一来,按道理说,一个文件中定义的 全局变量,可以在整个程序的任何地方被使用,举例说,如果A文件中定义了某全局变量,那么B文件中应可以使用该变量。修改我们的程序,加以验证:
1
2
3
4
5
6
7
//A.cpp
intmain()
{
i=100; //试图使用B中定义的全局变量
}
//B.cpp
inti;
编译结果如下:
1
2
3
4
5
Compiling...
A.cpp
C:\DocumentsandSettings\wangjian\桌面\tryextern\A.cpp(5):errorC2065:'i':undeclaredidentifier
Errorexecutingcl.exe.
A.obj-1error(s),0warning(s)
编译错误。
其实出现这个错误是意料之中的,因为文件中定义的 全局变量的可见性扩展到整个程序是在链接完成之后,而在编译阶段,他们的可见性仍局限于各自的文件。
编译器的目光不够长远,编译器没有能够意识到,某个 变量符号虽然不是本文件定义的,但是它可能是在其它的文件中定义的。
虽然 编译器不够有远见,但是我们可以给它提示,帮助它来解决上面出现的问题。这就是extern的作用了。
extern的原理很简单,就是告诉 编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的 全局变量,你要放行!”
我们为上面的错误程序加上extern关键字:
1
2
3
4
5
6
7
8
//A.cpp
externinti;
intmain()
{
i=100; //试图使用B中定义的全局变量
}
//B.cpp
inti;
顺利通过编译,链接。

3函数编辑

extern函数1
常见extern放在 函数的前面成为 函数声明的一部分,那么, C语言关键字extern在 函数的声明中起什么作用?
答案与分析:
如果 函数的声明中带有 关键字extern,仅仅是暗示这个 函数可能在别的源文件里定义,没有其它作用。即下述两个 函数声明没有明显的区别:
extern int f(); 和int f();
当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明 函数,在一些复杂的项目中,我比较习惯在所有的 函数声明前添加extern修饰。
extern函数2
函数提供方单方面修改 函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时 编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会造成系统错误,这种情况应该如何解决?
答案与分析:
目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部 接口的声明,然后调用包涵该文件的头文件,从而省去extern这一步。以避免这种错误。
宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。
extern “C”
C++环境下使用C 函数的时候,常常会出现 编译器无法找到obj模块中的C 函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?
答案与分析:
C++语言在编译的时候为了解决 函数的多态问题,会将 函数名和参数联合起来生成一个中间的 函数名称,而 C语言则不会,因此会造成链接时找不到对应 函数的情况,此时C 函数就需要用extern “C”进行链接指定,这告诉 编译器,请保持我的名称,不要给我生成用于链接的中间 函数名。
下面是一个标准的写法:
//在.h文件的头上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
//.h文件结束的地方
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
C++中extern c的深层探索
C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的 全局变量函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++保留了一部分过程式语言的特点(被世人称为“不彻底地 面向对象”),因而它可以定义不属于任何类的 全局变量函数。但是,C++毕竟是一种 面向对象的程序设计语言,为了支持 函数重载,C++对 全局函数的处理方式与C有明显的不同。
2.从标准头文件说起
某企业曾经给出如下的一道面试题:
面试题
为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
分析
显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。
那么
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
的作用又是什么呢?我们将在下文一一道来。
3.深层揭密extern "C"
-----------------------------对于extern "C" 可以看"C 与 C++ 的混合编程"
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011170660/article/details/38334431

智能推荐

3.VTK坐标系统以及VTK管线-程序员宅基地

文章浏览阅读187次。这个专栏是用于记录我在学习VTK过程中的一些心得体会。参考的资料主要有以下三个:1. 张晓东 罗火灵《VTK图形图像开发进阶》3. 沈子恒 《VTK 三维数据渲染进阶》遇到的一个大问题就是由于版本更新,这些资料中很多代码无法正常运行,需要进行一定的修改,所以这个专栏会记录下来我修改后的程序代码,以便于我之后温习。也希望能给和我有同样困扰的小伙伴们一些帮助。我使用的版本:VTK9 + VS2022在上一篇中我介绍了VTK的流水线设计。_vtk

一篇文章搞懂前端缓存_前端资源请求来源(disk cache)-程序员宅基地

文章浏览阅读354次。搞懂前端缓存总的来说:如果开启了Service Worker首先会从Service Worker中拿如果新开一个以前打开过的页面缓存会从Disk Cache中拿(前提是命中强缓存)刷新当前页面时浏览器会根据当前运行环境内存来决定是从 Memory Cache 还是 从Disk Cache中拿(可以看到下图最后几个文件有时候是从 Memory Cache中拿有时候是从Disk Cache中拿) 注意:以上回答全部基于chrome浏览器搞懂前端缓存前端缓存/后端缓存扯了些没用的,我们先进入定_前端资源请求来源(disk cache)

汇编语言源程序基础分析--LED灯闪烁_汇编语言写led闪烁延时怎么算-程序员宅基地

汇编语言源程序基础分析--LED灯闪烁。摘要长度为30到160字符数之间。

【Linux】创建文件链接(ln | 符号链接和硬链接)_为该文件创建符号链接,并命名为“姓名首字母+l”。-程序员宅基地

文章浏览阅读8.2k次,点赞9次,收藏58次。本文将介绍如何在 Linux 中为文件创建硬练级和符号(软)链接,并探讨两者之间的相同和不同之处。符号链接与 Windows 中的快捷方式很像。Windows 中的快捷方式是一个带箭头的图标,空间占用一般很小,仅仅提供了一个指向目标文件的通道,Linux 中的符号链接也类似。符号链接应用很灵活,可以为任意文件创建符号链接。虽然硬链接名字里面也有链接二字,但其与符号链接还是相当不一样的。要理解硬链接,我们得先了解若干基本概念。首先我们得搞明白 Linux 的文件是如何存储中,在 Linux 中当划分磁盘分区并_为该文件创建符号链接,并命名为“姓名首字母+l”。

openMV镜头下的STM32小车寻迹原理及其调试注意事项(附openMV代码)_摄像头循迹小车的原理-程序员宅基地

文章浏览阅读3.2w次,点赞75次,收藏753次。STM32各类小车工作原理及学习http://www.yahboom.com/study/bc-32简介:循迹小车由三轮或四轮小车和摄像头两大部分组成,通过镜头识别路径,将其得到的图像做处理并发送给小车,小车对应做出动作。寻迹小车核心组成:openMV和STM3F1。涉及主要知识:STM32相关知识(学习视频 https://ke.qq.com/course/279403);openM..._摄像头循迹小车的原理

CCS软件安装教程_ccs是什么软件-程序员宅基地

文章浏览阅读10w+次,点赞82次,收藏352次。Code Composer Studio 是一种集成开发环境 (IDE),简称CCS软件。支持 TI 的微控制器和嵌入式处理器产品系列。Code Composer Studio 包含一整套用于开发和调试嵌入式应用的工具_ccs是什么软件

随便推点

Android Launcher 源码解析与启动流程_android launcher源码解析-程序员宅基地

文章浏览阅读170次。通过对Launcher的各个核心组件的分析和调用关系的解析,我们可以更好地理解Launcher的运行机制。Launcher作为Android系统的入口界面,扮演着重要的角色,为用户提供了良好的桌面体验。DragLayer的创建:LauncherModel初始化完成后,系统会创建DragLayer对象,并将其作为Launcher的根视图。Android的Launcher(启动器)是用户与设备交互的第一个界面,它负责加载并展示主屏上的应用图标、小部件和壁纸等内容。二、Launcher的启动流程。_android launcher源码解析

Unity显示360度全景照片_unity raw image显示全景图-程序员宅基地

文章浏览阅读2.5w次,点赞6次,收藏75次。Unity显示360度全景,其实就是把全景图片贴到一个球上,只不过,这个球是从里往外看。先把摄像机移动到坐标(0,0,0)。添加一个球体,坐标也是(0,0,0)把全景图片拖到球体上,这个时候,因为贴图在外面,所以还看不到新建一个着色器Shader "InsideVisible" {Propertie_unity raw image显示全景图

简单LVS搭建_lvs本机搭建-程序员宅基地

文章浏览阅读457次。编号服务器名称网卡Mmasterens33: 192.168.1.100/24lo:192.168.1.100/32 bro 192.168.1.100W1web 1ens33:192.168.1.23/24lo:192.168.1.100/32 bro 192.168.1.100W2web 2ens33:192.168.1.24/24lo:192.168.1.100/32 bro 192.168.1.100为所有服务器安装必备软件mv /etc/yum..._lvs本机搭建

ctf-misc-buuctf刷题记录(初步)_ctf elf-程序员宅基地

文章浏览阅读2.1k次。镜子里的世界使用stegSolve查看一下,就找到了flagningen:题目提到了四位密码,会联想到是否需要破解密码,点开文件时一个图片,就拿去kali里面,先binwalk一下,有东西,再foremost一下,东西就out出来了。果真和猜想一样,得到一个zip文件,拿去暴力破解,得到密码,输入进去。成功得到flag。小明的保险箱:和前一道题一样,题目提示有四位密码,获得一个简单的图片,扔进kali,操作和前一题一样获得out,然后暴力破解,顺利得到密码,输入进去,成功得到flag。._ctf elf

CSS3动画效果详解_css3 动画细说-程序员宅基地

文章浏览阅读889次,点赞22次,收藏18次。在CSS3中,animation属性用于实现元素的动画。animation属性跟transition属性在功能实现上是非常相似的,都是通过改变元素的属性值来实现动画效果。但是,这两者实际上有着本质的区别。CSS3中的animation属性就是为了解决这些问题而提出的。animation属性可以通过控制关键帧来控制动画的每一步,从而实现更为复杂的动画效果。使用animation属性实现CSS3动画需要两步。_css3 动画细说

浓度梯度与偏倚随机行走-程序员宅基地

文章浏览阅读226次。原文:Concentration Gradients什么是浓度梯度?溶液中化学物质的浓度指单位体积溶液中有多少这种分子。梯度用来测量某量随地点而变的变化程度。浓度梯度当然就表示浓度随地点而变的变化程度。现在我们举几个例子。我们把单个分子画成小蓝点。如果分子浓度均匀,即没有梯度,溶液就如下图所示:没有浓度梯度的溶液下面再看个浓度有梯度的溶液,如下图所示,右边浓度高,左边浓度低:有浓度..._浓度扩散 随机行走

推荐文章

热门文章

相关标签