但是當(dāng)我運(yùn)行程序在VC6上時(shí),發(fā)現(xiàn)這個(gè)程序Link時(shí)會(huì)報(bào)錯(cuò),那是不是編譯器更新了呢?
轉(zhuǎn)本貼請(qǐng)把 win_hate 的名字寫上。
略談 c 語言中指針與數(shù)組的區(qū)別
數(shù)組與指針是兩個(gè)不同的概念,即使是從編譯的層面上來看。不過,在很多時(shí)候,兩者的用法極為相似。本文將討論兩者的區(qū)別。
一、理論分析
編譯器在處理指針與數(shù)組的時(shí)候,是區(qū)別對(duì)待的。
對(duì)于指針
p 是一個(gè)變量,所以編譯器要為之分配一個(gè)空間。
對(duì)于數(shù)組:
a 是一個(gè)地址,編譯器會(huì)為數(shù)組 a 分配一個(gè)空間,但不會(huì)為 a 本身分配空間,在使用到a的地方,會(huì)被替換為一個(gè)地址+屬性,其結(jié)果為一個(gè)"常量指針"。
在對(duì)一個(gè)指針變量進(jìn)行 dereferance 的時(shí)候,比如 (*p)。編譯器首先要得到 p 的地址,從中取值,然后把得到的值作為地址,再取值。類似如下匯編:
lea (p), %esi /* this is &p */
mov (%esi), %edi /* this is p */
mov (%edi), %eax /* this is p[0] */
或者,更簡(jiǎn)單的
mov (p), %esi /* this is p */
mov (%esi), %eax /* and this is p[0] */
相比之下,數(shù)組的引用
則省去了取 a 地址的過程,符號(hào) a 代表一個(gè)地址,這個(gè)地址不存放在任何變量中!
lea (a), %esi /* this is a */
mov (%esi), %eax /* this is a[0] */
或更簡(jiǎn)單的:
mov (a), %esi /* this is a[0] */
熟悉匯編的人,容易從看出,區(qū)別是大的。
二、兩個(gè)例子
第一個(gè)例子,演示 "把數(shù)組聲明為指針" 是如何使程序崩潰的。
file: 1.c
int a[10]={0};
file: 2.c
int
main ()
{
extern int *a;
printf ("%d\n", a[0]);
return 0;
}
運(yùn)行這個(gè)程序,Segmentation fault
在模塊1.c 中, a 被定義為一個(gè)數(shù)組,但在模塊 2.c中,a被聲明為指針。所以編譯器在處理 printf ("%d\n", a[0]) 時(shí):
認(rèn)為 a 是一個(gè)指針,所以先取其地址&a,然而,a 實(shí)際是個(gè)數(shù)組,&a 就是 a本身,所的 &a 是 a 的首地址。
然后編譯器取 指針a的值,這實(shí)際上是 得到的是數(shù)組的第一個(gè)元素 a[0] ,值為0!也就是,編譯器得到了一個(gè) 0 指針,最后,編譯器對(duì)其derefrence,崩潰!
第二個(gè)例子演示“把指針聲明為數(shù)組”如何的到錯(cuò)誤的數(shù)據(jù):
file: 3.c
int *pa = (int *)0;
f ()
{
printf ("%x\n", &pa);
}
file: 4.c
int
main ()
{
extern int pa[];
printf ("%p\n", pa);
printf ("%d\n", pa[0]);
f ();
return 0;
}
在這個(gè)例子中, pa 被定義為一個(gè)指針,并初始化為0, 但在另一個(gè)模塊中,被聲明為一個(gè)數(shù)組.
編譯器在處理 printf ("%p\n", pa) 時(shí),認(rèn)為 pa 是數(shù)組,所以直接打印符號(hào)pa的值,此值為指針pa的地址!
編譯器在處理 printf ("%p\n", pa[0]) 時(shí),認(rèn)為 pa 是數(shù)組,以符號(hào) pa 對(duì)應(yīng)的值加一個(gè)偏移0,并取其值,得到的實(shí)際上是 指針 pa 的值 即 0.
三、總結(jié)
我羅羅嗦嗦地講了半天,如果還不清楚,請(qǐng)研究一下我給出的代碼。
[參考文獻(xiàn)]
[1] Peter Van Der Linden, <<Expert C Programming --- Deep C Secrets>;>;