extern-c,c++关键字(1)

1 篇文章 0 订阅

为什么还要写关键字呢?刚学C时,其实没有想过和怀疑过书本上讲的比实际中的差多少,总觉得我所欠缺的只是算法和经验,根本没发现,其实就连最基础的关键字,也是那么重要,因此在以后的写作中,我会逐步地将部分关键字的用法和注意事项写下来.

 

自从看过<C语言深度解析>之后,我就觉得,在大体学完一门语言,了解基本语法之后,也能通过各式各样的类,方法做出不错的软件出来,但是对于这门语言来说,还不算精通,甚至对于使用触类旁通快速上手的人来说,可能连熟练都算不上.

 

最会带帽子的关键字----extern

 

extern,外面的、外来的意思。那它有什么作用呢?举个例子:假设你在大街上看到一个黑皮肤绿眼睛红头发的美女(外星人?)或者帅哥。你的第一反应就是这人不是国产的。extern 就相当于他们的这些区别于中国人的特性。extern 可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,下面的代码用到的这些变量或函数是外来的,不是本文件定义的,提示编译器遇到此变量和函数时在其他模块中寻找其定义。就好比在本文件中给这些外来的变量或函数带了顶帽子,告诉本文件中所有代码,这些家伙不是土著。那你想想extern 修饰的变量或函数是定义还是声明?

[注]全局变量,全局应该是整个程序都能用,那为什么直接用不行呢?因为编译器先对单个的文件进行编译,这时如果一个变量没有定义,就会出现编译错误.若编译完没问题,之后编译器会链接obj文件,这是如果有两个obj中有名字相同的全局变量,这时又会出现链接错误.所以如何在另一个文件中使用本文件中定义的全局变量呢,这时就需要extern关键字,通过它来使编译器在编译阶段不对未在本文件中定义的全局变量的使用作出未定义错误处理.

[注]全局变量的作用域是从定义开始,到本文件结束.


看列子:

A.c 文件中定义:

int i = 10;

void fun(void)

{

    //code

}

B.c 文件中用extern 修饰:

extern int i;//写成i = 10;行吗?

[注]

extern void fun(void);//两个void 可否省略?

[注]前一个不行,后一个行

C.h 文件中定义:

int j = 1;

int k = 2;

D.c 文件中用extern 修饰:

extern double j;//这样行吗?为什么?

[注]不行

[注]extern int j = 1;也不行,这样算定义,链接时会出错

j = 3.0;//这样行吗?为什么?

[注]不声明,直接使用不行

 

定义为数组,声明为指针

文件1 中定义如下:
char a[100];
文件2 中声明如下:
extern char *a;
这里,文件1 中定义了数组a,文件2 中声明它为指针。这有什么问题吗?平时不是总说数组与指针相似,甚至可以通用吗?但是,很不幸,这是错误的。通过上面的分析我们也能明白一些,但是“革命尚未成功,同志仍需努力”。你或许还记得我上面说过的话:数组就是数组,指针就是指针,它们是完全不同的两码事!他们之间没有任何关系,只是经常穿着相似的衣服来迷惑你罢了。下面就来分析分析这个问题:在第一章的开始,我就强调了定义和声明之间的区别,定义分配的内存,而声明没有。定义只能出现一次,而声明可以出现多次。这里extern 告诉编译器a 这个名字已经在别的文件中被定义了,下面的代码使用的名字a 是别的文件定义的。再回顾到前面对于左值和右值的讨论,我们知道如果编译器需要某个地址(可能还需要加上偏移量)来执行某种操作的话,它就可以直接通过开锁动作(使用“*”这把钥匙)来读或者写这个地址上的内存,并不需要先去找到储存这个地址的地方。相反,对于指针而言,必须先去找到储存这个地址的地方,取出这个地址值然后对这个地址进行开锁(使用“*”这把钥匙)。如下图:

 

这就是为什么extern char a[]与extern char a[100]等价的原因。因为这只是声明,不分配空间,所以编译器无需知道这个数组有多少个元素。这两个声明都告诉编译器a 是在别的文件中被定义的一个数组,a 同时代表着数组a 的首元素的首地址,也就是这块内存的起始地址。数组内地任何元素的的地址都只需要知道这个地址就可以计算出来。但是,当你声明为extern char *a 时,编译器理所当然的认为a 是一个指针变量,在32 位系统下,占4 个byte。这4 个byte 里保存了一个地址,这个地址上存的是字符类型数据。虽然在文件1 中,编译器知道a 是一个数组,但是在文件2 中,编译器并不知道这点。大多数编译器是按文件分别编译的,编译器只按照本文件中声明的类型来处理。所以,虽然a 实际大小为100 个byte,但是在文件2 中,编译器认为a 只占4 个byte。我们说过,编译器会把存在指针变量中的任何数据当作地址来处理。所以,如果需要访问这些字符类型数据,我们必须先从指针变量a 中取出其保存的地址。如下图:

 

定义为指针,声明为数组

显然,按照上面的分析,我们把文件1 中定义的数组在文件2 中声明为指针会发生错误。同样的,如果在文件1 中定义为指针,而在文件中声明为数组也会发生错误:
文件1
char *p = “abcdefg”;
文件2
extern char p[];
在文件1 中,编译器分配4 个byte 空间,并命名为p。同时p 里保存了字符串常量“abcdefg”的首字符的首地址。这个字符串常量本身保存在内存的静态区,其内容不可更改。在文件2中,编译器认为p 是一个数组,其大小为4 个byte,数组内保存的是char 类型的数据。在文件2 中使用p 的过程如下图:

[注]我觉得上面这两段写的并不好,一开始说编译不通过,这是对的,然后开始说原因,但是从原因的内容来看,却是程序运行时会出现错误,所以作者应该是想通过假设编译通过,然后程序运行也会出错,来说明以上两种做法不对.

[注]修改下作者的说法,假如编译通过,运行时这段代码中声明的是指针,去那段代码中声明的数组中取值,就会这样取,先取数组的前四个字节作为地址,然后去这个地址中取值来使用,另一种就会将指针中保存的地址作为数组元素.所以这两种用法都是错的.

[注]再说平时不是总说数组与指针相似,甚至可以通用的这种情况,那是因为你定义的是指针,无论怎么用,程序都会把它当成指针处理,你定义的是数组,程序就会把它当成数组去用,而不会出现明明定义的是指针,却声明成数组这种混乱的情况.

[注]上面基本就是C中extern的绝大部分了,但是C++中还有一种重要用法.会在"extern-c,c++关键字(2)"中详细说明.

本文绝大部分出自陈正冲的<C语言深度解剖>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值