友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
荣耀电子书 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

[免费下载 c语言深度解剖[1]-第章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!



2中声明它为指针。这有什么问题吗?平时不是总说数
组与指针相似,甚至可以通用吗?但是,很不幸,这是错误的。通过上面的分析我们也能
明白一些,但是“革命尚未成功,同志仍需努力”。你或许还记得我上面说过的话:数组就
是数组,指针就是指针,它们是完全不同的两码事!他们之间没有任何关系,只是经常穿
着相似的衣服来迷惑你罢了。下面就来分析分析这个问题:

在第一章的开始,我就强调了定义和声明之间的区别,定义分配的内存,而声明没有。
定义只能出现一次,而声明可以出现多次。这里 
extern告诉编译器 
a这个名字已经在别的文
件中被定义了,下面的代码使用的名字 
a是别的文件定义的。再回顾到前面对于左值和右值
的讨论,我们知道如果编译器需要某个地址(可能还需要加上偏移量)来执行某种操作的
话,它就可以直接通过开锁动作(使用“*”这把钥匙)来读或者写这个地址上的内存,并不
需要先去找到储存这个地址的地方。相反,对于指针而言,必须先去找到储存这个地址的
地方,取出这个地址值然后对这个地址进行开锁(使用“*”这把钥匙)。如下图:


这就是为什么externchara''与externchara'100'等价的原因。因为这只是声明,不分配这就是为什么externchara''与externchara'100'等价的原因。因为这只是声明,不分配
空间,所以编译器无需知道这个数组有多少个元素。这两个声明都告诉编译器 
a是在别的文
件中被定义的一个数组, 
a同时代表着数组 
a的首元素的首地址,也就是这块内存的起始地
址。数组内地任何元素的的地址都只需要知道这个地址就可以计算出来。

但是,当你声明为 
externchar*a时,编译器理所当然的认为 
a是一个指针变量,在 
32位系
统下,占 
4个 
byte。这 
4个 
byte里保存了一个地址,这个地址上存的是字符类型数据。虽
然在文件 
1中,编译器知道 
a是一个数组,但是在文件 
2中,编译器并不知道这点。大多数
编译器是按文件分别编译的,编译器只按照本文件中声明的类型来处理。所以,虽然 
a实际
大小为 
100个 
byte,但是在文件 
2中,编译器认为 
a只占 
4个 
byte。

我们说过,编译器会把存在指针变量中的任何数据当作地址来处理。所以,如果需要
访问这些字符类型数据,我们必须先从指针变量 
a中取出其保存的地址。如下图:



4。3。3。2,定义为指针,声明为数组
显然,按照上面的分析,我们把文件 
1中定义的数组在文件 
2中声明为指针会发生错误。
同样的,如果在文件 
1中定义为指针,而在文件中声明为数组也会发生错误:
文件 
1 
char*p 
= 
“abcdefg”;
文件 
2 


externcharp'';
在文件 
1中,编译器分配 
4个 
byte空间,并命名为 
p。同时 
p里保存了字符串常量“abcdefg”
的首字符的首地址。这个字符串常量本身保存在内存的静态区,其内容不可更改。在文件 
2
中,编译器认为 
p是一个数组,其大小为 
4个 
byte,数组内保存的是 
char类型的数据。在
文件 
2中使用 
p的过程如下图: 


p 


0x0000FF00
p

0x000x000x000xFFpcharp'0'
p'1'p'2'p'3'
4char0x000x000xFF0x00p'i'p
4。3。4,指针和数组的对比
通过上面的分析,相信你已经知道数组与指针的的确确是两码事了。他们之间是不可
以混淆的,但是我们可以“以 
XXXX的形式”访问数组的元素或指针指向的内容。以后一
定要确认你的代码在一个地方定义为指针,在别的地方也只能声明为指针;在一个的地方
定义为数组,在别的地方也只能声明为数组。切记不可混淆。下面再用一个表来总结一下
指针和数组的特性:

指针数组
保存数据的地址,任何存入指针变量 
p的数
据都会被当作地址来处理。p本身的地址由
编译器另外存储,存储在哪里,我们并不知
保存数据,数组名 
a代表的是数组首元素的
首地址而不是数组的首地址。&a才是整个数
组的首地址。a本身的地址由编译器另外存


道。储,存储在哪里,我们并不知道。
间接访问数据,首先取得指针变量 
p的内容,
把它作为地址,然后从这个地址提取数据或
向这个地址写入数据。指针可以以指针的形
式访问*(p+i);也可以以下标的形式访问 
p'i'。
但其本质都是先取 
p的内容然后加上 
i*sizeof(类型)个 
byte作为数据的真正地址。
直接访问数据,数组名 
a是整个数组的名字,
数组内每个元素并没有名字。只能通过“具
名+匿名”的方式来访问其某个元素,不能把
数组当一个整体来进行读写操作。数组可以
以指针的形式访问*(a+i);也可以以下标的形
式访问 
a'i'。但其本质都是 
a所代表的数组首
元素的首地址加上 
i*sizeof(类型)个 
byte作为
数据的真正地址。
通常用于动态数据结构通常用于存储固定数目且数据类型相同的元
素。
相关的函数为 
malloc和 
free。隐式分配和删除
通常指向匿名数据(当然也可指向具名数据)自身即为数组名

4。4,指针数组和数组指针
4。4。1,指针数组和数组指针的内存布局
初学者总是分不出指针数组与数组指针的区别。其实很好理解:

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身
决定。它是“储存指针的数组”的简称。

数组指针:首先它是一个指针,它指向一个数组。在 
32位系统下永远是占 
4个字节,
至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

下面到底哪个是数组指针,哪个是指针数组呢: 


A),int 
*p1'10'; 
B),int 
(*p2)'10';
每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。 
“''”的优先级比“ 
*”要高。 
p1先与“ 
''”结合,构成一个数组的定义,数组名为 
p1,int*
修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含 
10个
指向 
int类型数据的指针,即指针数组。至于 
p2就更好理解了,在这里“()”的优先级比 
“''”高,“*”号和 
p2构成一个指针的定义,指针变量名为 
p2,int修饰的是数组的内容,
即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚 
p2是一个指
针,它指向一个包含 
10个 
int类型数据的数组,即数组指针。我们可以借助下面的图加深
理解:


4。4。2,int 
(*)'10'p2…也许应该这么定义数组指针
这里有个有意思的话题值得探讨一下:平时我们定义指针不都是在数据类型后面加上
指针变量名么?这个指针 
p2的定义怎么不是按照这个语法来定义的呢?也许我们应该这样
来定义 
p2: 


int 
(*)'10' 
p2; 


int 
(*)'10'是指针类型,p2是指针变量。这样看起来的确不错,不过就是样子有些别
扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量 
p2前移了
而已。你私下完全可以这么理解这点。虽然编译器不这么想。^_^

4。4。3,再论 
a和&a之间的区别
既然这样,那问题就来了。前面我们讲过 
a和&a之间的区别,现在再来看看下面的代
码: 


int 
main() 


{ 


chara'5'={'A';'B';'C';'D'}; 


char(*p3)'5'=&a; 


char(*p4)'5'= 
a; 


return0; 


} 



上面对 
p3和 
p4的使用,哪个正确呢?p3+1的值会是什么?p4+1的值又会是什么?
毫无疑问,p3和 
p4都是数组指针,指向的是整个数组。&a是整个数组的首地址,a
是数组首元素的首地址,其值相同但意义不同。在 
C语言里,赋值符号“ 
=”号两边的数据
类型必须是相同的,如果不同需要显示或隐式的类型转换。 
p3这个定义的“ 
=”号两边的数
据类型完全一致,而 
p4这个定义的“=”号两边的数据类型就不一致了。左边的类型是指
向整个数组的指针,右边的数据类型是指向单个字符的指针。在 
VisualC++6。0上给出如下
警告:warningC4047: 
'initializing': 
'char(*)'5''differsinlevelsof 
indirectionfrom 
'char*'。还好,
这里虽然给出了警告,但由于 
&a和 
a的值一样,而变量作为右值时编译器只是取变量的值,
所以运行并没有什么问题。不过我仍然警告你别这么用。
既然现在清楚了 
p3和 
p4都是指向整个数组的,那 
p3+1和 
p4+1的值就很好理解了。

但是如果修改一下代码,会有什么问题?p3+1和 
p4+1的值又是多少呢? 
int 
main() 
{ 


chara'5'={'A';'B';'C';'D'}; 
char(*p3)'3'=&a; 
char(*p4)'3'= 
a; 
return0; 




甚至还可以把代码再修改: 


int 
main() 


{ 
chara'5'={'A';'B';'C';'D'}; 
char(*p3)'10'= 
&a; 
char(*p4)'10'= 
a; 
return0; 



这个时候又会有什么样的问题?p3+1和 
p4+1的值又是多少?
上述几个问题,希望读者能仔细考虑考虑。

4。4。4,地址的强制转换
先看下面这个例子: 


structTest 


{ 
int 
Num; 
char 
*pcName; 



short 
sDate; 
char 
cha'2'; 
short 
sBa'4'; 


}*p;

假设 
p的值为 
0x100000。如下表表达式的值分别为多少? 
p 
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!