分类: C/C++
2007-07-20 18:32:41
/* pna.c */ printf("ep[3]=%c %p %p\n", ep[3], ep, &ep[3]);
printf("ea[3]=%c\n", ea[3]); /* 引起崩溃的罪魁祸首 */ |
|
gcc -g -o pna pna.c pna_nothing.c |
$ ./pna.exe a[3]=d 0x22ccd0 0x22ccd3 p[3]=d 0x403000 0x403003 ep[3]= 0x402008 0x40200b 8 [main] pna 3596 _cygtls::handle_exceptions: Error while dumping state (probably corrupted st ack) Segmentation fault (core dumped) |
|
根据以上结果,我们分析一下数组元素a[3]的引用过程:
1.编译器符号表具有地址 0x22cc90
2.运行时步骤1,0x22cc90+3=
0x22cc93
3.运行时步骤2,取地址0x22cc93的值d
其内存布局大致如下:
address |value |name
------------------------0x22cc90|'a' |a[0]
... |... |...0x22cc93|'d' |a[3]
接着我们分析一下p[3]的引用过程:
1.编译器符号表有一个符号p:它的地址为0x22cc8c
2.运行时步骤1,取地址0x22cc8c的内容
0x403000
3.运行时步骤2,0x403000+3=
0x403003
4.运行时步骤3,取地址0x403003的值d
其内存布局大致如下:address |value |name
------------------------0x22cc8c
|
0x403000
| p
... |... |...0x403000
|'a' |p[0]
... |... |...0x403003|'d' |p[3]
看出区别了么?数组引用是直接寻址,指针方式引用是间接寻址。
这就是一个细微的区别,如果不注意区别可能会导致致命的错误,就像接着对ea[3]的外部引用,把ea声明为一个指针,但实际上它是一个数组,对一个数组间接引用会产生什么后果呢?过程如下:1.编译器符号表有一个符号ea:它的地址为
0x402000
2.运行时步骤1,取地址
0x402000
的内容,编译器被告知ea是指针(其实是数组),于是采用间接寻址方式,作为32位平台,指针大小为4byte,因此取0x402000--0x402003范围内的值,即ASCII码abcd被解释成16进制值0x64636261(little-endian),作为寻址地址
3.运行时步骤2,偏移运算,因为ea被告知是指向char型的,故0x
64636261+3=
0x64636264
4.运行时步骤3,试图取
0x64636264地址的值
!@#$$%^$%&^&^,这你都敢取??越界了啦
Segmentation fault……
于是……
其内存布局大致如下:address |value |name
------------------------
0x64636264|xxxxxxxx|寻址ea+3到这,该值未知……
... |... |...0x402000
|
01100001
|不同的解释:61(hex) a(char),但ea却被不幸地解释成了&ea
0x402001 |01100010
|... :62(hex) b(char)
0x402002 |01100011
|...
:63(hex) c(char)
0x402003
|
01100100
|...
:64(hex) d(char)
呵呵,不知这么解释清楚了么?要修正这个错误很简单,老老实实把ea声明为数组就行了。
定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间。除非在定义时同时赋给指针一个字符串常量进行初始化。注意,只有对字符串常量才是如此,不能指望为浮点数或整型的常量分配空间。
ANSI C中,初始化指针时所创建的字符串常量被定义为只读。
而由字符串常量初始化的数组是可以修改的。
数组和指针在编译器处理时是不同的,在运行时的表示形式也是不一样的,并可能产生不同的代码。对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。