天下
记录修行的印记
水滴石穿C语言之extern声明辨析
水滴石穿C语言之extern声明辨析
作者:楚云风
1
、基本解释
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
另外,extern也可用来进行链接指定。
2
、问题:
extern
变量
在一个源文件里定义了一个数组:
char
a[
6
];
在另外一个文件里用下列语句进行了声明:
extern
char
*
a;
请问,这样可以吗?
答案与分析:
1
)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。
extern
char
*
a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern
char
a[ ]。
2
)、例子分析如下,如果a[]
=
"
abcd
"
,则外部变量a
=
0x61626364
、(abcd的ASCII码值),
*
a显然没有意义,
显然a指向的空间(
0x61626364
)没有意义,易出现非法内存访问。
3
)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。
4
)、extern用在变量声明中常常有这样一个作用,你在
*
.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在
*
.h中并用extern来声明。
3
、问题:
extern
函数1
常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?
答案与分析:
如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:
extern
int
f(); 和int f();
当然,这样的用处还是有的,就是在程序中取代include
"
*.h
"
来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。
4
、问题:
extern
函数2
当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决?
答案与分析:
目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。
宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。
5
、问题:
extern
"
C
"
在C
++
环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?
答案与分析:
C
++
语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern
"
C
"
进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
下面是一个标准的写法:
//
在.h文件的头上
#ifdef __cplusplus
extern
"
C
"
{
#endif
/* __cplusplus */
…
…
//
.h文件结束的地方
#ifdef __cplusplus
}
#endif
/* __cplusplus */
extern
"
C
"
extern
"
C
"
包含双重含义。
其一:被它修饰的目标是“
extern
”的;
其二:被它修饰的目标是
"
C
"
的。
1
)被extern
"
C
"
限定的函数或变量是extern类型的;
extern是C
/
C
++
语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其他模块中使用。
注意:
extern
int
a;
仅仅是在声明一个变量,并不是定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
通常,在模块的头文件中对模块提供给其他模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错,它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern
"
C
"
修饰。
2
)被extern
"
C
"
修饰的变量和函数是按照C语言方式编译和连接的
作为一种面向对象的语言,C
++
支持函数重载,而过程式语言C则不支持。函数被C
++
编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void
foo(
int
x,
int
y);
该函数被C编译器编译后在符号库中的名字为_foo,而C
++
编译器则会产生像_foo_int_int之类的名字(不同的编译器可能产生的名字不同,但是都采用了相同的机制)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C
++
就是靠这种机制来实现函数重载的。例如,在C
++
中,函数void foo(
int
x,
int
y)与void foo(
int
x,
float
y)编译产生的符号是不相同的,后者为_foo_int_float。
extern
"
C
"
作用:实现C
++
与C及其它语言的混合编程。
3
)
extern
"
C
"
的惯用法
A)在C
++
中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern
"
C
"
{
#include
"
cExample.h
"
}
而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern
"
C
"
声明,在.c文件中包含了extern
"
C
"
时会出现编译语法错误。
例如:
/*
c语言头文件:cExample.h
*/
#ifndef C_EXAMPLE_H
#define
C_EXAMPLE_H
extern
int
add(
int
x,
int
y);
#endif
/*
c语言实现文件:cExample.c
*/
#include
"
cExample.h
"
int
add(
int
x,
int
y)
{
return
x
+
y;
}
//
c++实现文件,调用add:cppFile.cpp
extern
"
C
"
{
#include
"
cExample.h
"
}
int
main(
int
argc,
char
*
argv[])
{
add(
2
,
3
);
return
0
;
}
B)在C中引用C
++
语言中的函数和变量时,C
++
的头文件需添加extern
"
C
"
,但是在C语言中不能直接引用声明了extern
"
C
"
的头文件,应该仅将C文件中将C
++
中定义的extern
"
C
"
函数声明为extern类型。
例如:
//
C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define
CPP_EXAMPLE_H
extern
"
C
"
int
add(
int
x,
int
y);
#endif
//
C++实现文件 cppExample.cpp
#include
"
cppExample.h
"
int
add(
int
x,
int
y)
{
return
x
+
y;
}
/*
C实现文件cFile.c
*/
//
这样会编译出错:#include "cppExample.h"
extern
int
add(
int
x,
int
y);
int
main(
int
argc,
char
*
argv[]) {
add(
2
,
3
);
return
0
;
}
extern
"
C
"
的用法
链接指示符extern
"
C
"
c
++
编译的时候,对函数名进行修饰,用于实现函数充载,而c里面没有这个,所以需要用extern
"
C
"
在对头文件进行声明的时候加以区分。这个用于链接的时候进行函数名查找。
编写的链接指示符有两种形式既可以是单一语句single statement 形式,
也可以是复合语句compound statement 形式
//
单一语句形式的链接指示符
extern
"
C
"
void
exit(
int
);
//
复合语句形式的链接指示符
extern
"
C
"
{
int
printf(
const
char
*
);
int
scanf(
const
char
*
);
}
//
复合语句形式的链接指示符
extern
"
C
"
{
#include
<
cmath
>
}
posted on 2010-10-23 18:41
天下
阅读(313)
评论(0)
编辑
收藏
引用
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
网站导航:
博客园
IT新闻
BlogJava
博问
Chat2DB
管理
<
2012年4月
>
日
一
二
三
四
五
六
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
导航
首页
新随笔
联系
聚合
管理
统计
随笔 - 331
文章 - 0
评论 - 58
引用 - 0
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
(4)
给我留言
查看公开留言
查看私人留言
随笔分类
(378)
Android
(rss)
C#(26)
(rss)
C/C++(73)
(rss)
C++必杀技法(5)
(rss)
C++模板(7)
(rss)
C++设计模式(1)
(rss)
COM/ATL(1)
(rss)
Java(10)
(rss)
kernel & Driver(13)
(rss)
Linux Shell(6)
(rss)
Linux编程(36)
(rss)
Linux使用(13)
(rss)
MySQL(5)
(rss)
Python(13)
(rss)
QT(30)
(rss)
Socket(15)
(rss)
WCF(2)
(rss)
Win32(58)
(rss)
WTL(8)
(rss)
第三方库(1)
(rss)
汇编语言(6)
(rss)
加密解密(6)
(rss)
经济、地理、历史(1)
(rss)
开发工具(16)
(rss)
逆向工程(4)
(rss)
数据库(2)
(rss)
算法(14)
(rss)
通信技术(1)
(rss)
图像处理(3)
(rss)
无意人生(2)
(rss)
随笔档案
(329)
2021年8月 (1)
2021年3月 (1)
2020年1月 (2)
2019年12月 (2)
2019年8月 (1)
2019年6月 (1)
2019年2月 (1)
2018年4月 (2)
2017年5月 (2)
2016年12月 (2)
2016年10月 (1)
2016年9月 (5)
2016年8月 (1)
2016年7月 (4)
2016年6月 (6)
2016年5月 (5)
2016年4月 (1)
2016年3月 (3)
2016年1月 (5)
2015年12月 (19)
2015年11月 (1)
2015年10月 (2)
2015年8月 (3)
2015年7月 (4)
2014年9月 (1)
2014年7月 (1)
2014年4月 (2)
2014年3月 (6)
2013年12月 (1)
2013年11月 (2)
2013年8月 (1)
2013年7月 (3)
2013年6月 (7)
2013年5月 (3)
2013年4月 (12)
2013年3月 (19)
2013年2月 (1)
2013年1月 (1)
2012年12月 (4)
2012年11月 (2)
2012年10月 (9)
2012年9月 (3)
2012年8月 (26)
2012年7月 (6)
2012年6月 (8)
2012年5月 (7)
2012年4月 (5)
2012年3月 (7)
2012年2月 (10)
2012年1月 (8)
2011年12月 (4)
2011年11月 (12)
2011年10月 (5)
2011年9月 (11)
2011年8月 (4)
2011年6月 (3)
2011年5月 (4)
2011年4月 (3)
2011年3月 (11)
2011年2月 (2)
2011年1月 (5)
2010年12月 (9)
2010年11月 (10)
2010年10月 (16)
链接
advdbg.org
bbs.hackav.com
CodeProject
cplusplus.com
en.literateprograms.org
http://www.cnblogs.com/chengmo/
nirsoft
stackoverflow.com
Sysinternals 实用工具索引
vicchina.51.net
风雪之隅
看雪学院
最新随笔
1. 单文件AES加解密算法
2. Qt状态机代码例子
3. JavaFX BindingTest3
4. JavaFX PropertyTest2
5. 2、Pass a string to/from Java to/from CTag(s): JNI
6. 1、Use native code through JNI (HelloWorld)
7. Oracle分页
8. c#条件编译
9. spring 官方下载地址(Spring Framework 3.2.x&Spring Framework 4.0.x)
10. spring源码分析(2)AnnotationMethodHandlerAdapter
搜索
最新评论
1. re: STL中map,vector等线程安全一个简单的个人想法
看到楼主在进步了...
这样确实不可以...
需要锁之类的
--Allen
2. re: Windows 7 With SP1 MSDN官方简体中文企业版(32/64位)
很好...感谢mson给我们的分享.太开心呀!我的新笔记本可装双系统了!
--zhangmon
3. re: windows7 下载地址
kpoiuzt
--ghjhgfd
4. re: windows路径操作API函数
>> PathUnquoteSpaces
>> 去除路径中的首尾空格
这个应该去除路径两侧的引号……
--賈可
5. re: shell遍历文件夹
@shark
3Q,已经更正
--天下
Powered by:
C++博客
Copyright © 天下