陣列名和指標的區別

2021-08-08 15:45:32 字數 4489 閱讀 8235

這個問題也糾結我蠻久,看到這篇不錯的部落格就轉過來啦。

實際上關於陣列與指標的區別這個問題在《c專家程式設計》已經有很詳細的闡釋,但我想用自己的語言說一說我的理解。

最近在做資料結構課設,其中乙個函式發生了令人費解的錯誤,簡化後的**如下:

#include 

int main()

程式執行到 printf 語句後便會掛掉,除錯時會提示乙個sigsegv訊號,根據原來的經驗,這時程式試圖訪問本不應該訪問的記憶體。 

原來在 c 語言課堂上老師經常提到陣列就是乙個指標,指標也可以像陣列那樣用使用中括號的方式來進行記憶體訪問。以這樣的想法來分析前面的程式:foo 是乙個字元指標,即 foo 的值即為「abcde」的首字元「a」的位址,*foo 即為 『a』;那麼 foo 這個指標一定存在某個記憶體單元,&foo獲得這個記憶體單元的位址,即 pfoo 是指向 foo 的指標,那麼*pfoo 得到 foo,*(*pfoo)應該得到『a』了;這樣理解的話,程式是不應該有問題的。 

下面我們使用指標代替陣列來實現上面的程式:

#include 

#include

int main()

程式這次執行結果和預料的相同,輸出乙個字母a。由此可見,陣列就是指標,這種說法是錯誤的。

有人認為陣列是乙個靜態常量,即陣列名代表乙個靜態的位址值,在編譯時確定,下面**可以證偽這種說法

int main()

; static

char *p = foo;

return

0;}

使用 gcc 編譯時會有以下錯誤:

notconstant

可見陣列名並不是代表乙個靜態量,並非位址常量。如果定義 foo 時加上 static 限定符,編譯就會通過,此時陣列名才代表了乙個靜態量。

請看以下**:

int main()

; char * const bar; //為什麼是這種寫法,請自行查閱相關資料

char *baz;

foo = baz; /* 1 */

bar = baz; /* 2 */

return

0;}

gcc 編譯時錯誤資訊為:

/* 1 */ error: incompatible types when assigning to

type

'char[1]' from type

'char *'

/* 2 */ error: assignment of read-only variable

'bar'

1 2 兩處出錯資訊並不相同,若陣列為動態常量指標,出錯資訊應像 2 那樣。

陣列既不是靜態常量,也不是指標,那麼陣列是什麼?

首先補充一些左值和右值的知識,引用《c專家程式設計》中的一段話:

出現在賦值符左邊的符號有時被稱為左值,出現在賦值符右邊的符號有時被稱為右值。編譯器為每個變數分配乙個位址(左值)。這個位址在編譯時可知,而且該變數在執行時一直儲存於這個位址。相反,儲存於變數中的值(它的右值)只有在執行時才可知。如果需要用到變數中儲存的值,編譯器就發出指令從指定位址讀入變數值並將它存於暫存器中。

我對左值的理解和書上有些區別,我把這裡的「符號」稱為「物件」,每乙個符號都代表乙個物件,物件與位址是一一對應的。即如果宣告了 int a,那麼 a 作為乙個左值時,a 即代表這個儲存在某個特定的位址的物件,對這個物件賦值即為把值放在這個特定的位址;a 作為右值時即代表 a 的內容,就是乙個單純的值,而不是物件。乙個值是不能作為左值的,比如乙個常數 1, 1 = a 這樣的賦值語句是無法編譯通過的。在我看來,「左值」義同「物件」,「右值」義同「值」,所以下面「左值」和「物件」指的是相同的東西。但是「左值」又有乙個子集:「可修改的左值」,只有這個子集中的東西才能放在賦值號左邊,因此我認為將引用中的第一句話修改為「出現在賦值符左邊的符號有時被稱為可修改的左值」更能表達其實際的意思。為什麼要引出這個子集,為的就是要把陣列分出來,陣列是左值,但並不是可修改的左值,因此你也不能直接把陣列名放在等號左邊進行賦值。

我先把結論放在這裡,然後在進行分析:陣列就是陣列,乙個陣列名就代表乙個陣列物件,這個物件內可以有乙個或多個元素,每個元素型別都相同;正如 int 就是 int,乙個 int 變數名就代表乙個 int 型別物件。看到這裡,你可能要笑了,這不是什麼都沒說嗎,誰不知道陣列是這個意思啊,我想知道陣列和指標什麼關係。其實對陣列的認識就是這樣乙個返璞歸真過程,看我來慢慢解釋。 

以下**:

/* 1.c */

int main()

; int bar = 1;

return

0;}

使用 gcc 將其彙編並以 intel 格式輸出組合語言檔案:

gcc -s

-masm

=intel 1.c

關鍵部分為:

mov

dword

ptr[esp+8], 1

movdword

ptr[esp+12], 1

esp+8 位置就是那個 int foo,esp+12 位置就是那個 int bar。可見,給 int 陣列的賦值時就像給乙個 int 變數賦值一樣,並沒用指標來進行間接訪問,這個 int 陣列物件 foo 的記憶體位址在編譯時就確定了,是 esp+8;正如那個 int 物件 bar 一樣,它的記憶體位址在編譯時也確定了,是esp+12。 

以示區別,我將下面**同樣以組合語言輸出:

/* 2.c */

#include

int main()

彙編的關鍵部分為:

mov     dword ptr [esp], 4

call _malloc

mov dword ptr [esp+28], eax

mov eax, dword ptr [esp+28]

mov dword ptr [eax], 1

前兩句為 foo 分配記憶體空間,第三句將分配的記憶體空間位址值賦給 foo,foo 的位址為 esp+28,編譯時已知。下面是賦值部分,首先從 foo 那裡得到位址值,然後向這個位址賦值,這裡可以看出和給陣列賦值的差別,給陣列賦值時是將值直接賦到了陣列中,而不用從**得到陣列的位址。 

由上面可以看出,陣列更像乙個普通的變數,編譯時就知道了其位址,可以直接賦值。

陣列不能放在賦值號左邊,但陣列仍可以作為乙個左值或者說物件出現在語句中,乙個重要的例子就是取位址操作:&。取位址操作 &的運算元必須是乙個左值,而不能是乙個右值。比如乙個變數int a = 1,&a 就可以得到 a 的位址,但 &1 是非法的,乙個單純的數值是沒有位址的。那麼對於乙個int foo,&foo 會返回乙個什麼樣的值呢?自然是乙個指向陣列的指標咯,下面的程式可以看出來:

int main()

那個賦值語句一定會觸發乙個的錯誤,我們可以根據編譯輸出來確定它們的型別,錯誤為:

error: incompatible types when assigning to

type

'int[1]' from type

'int (*)[1]'

沒錯,&foo 返回資料型別為 int (*)[1],就是乙個指向陣列的指標。指向陣列?指向陣列的**呢?指向陣列物件首位址,正如乙個指向 int 物件的指標指向那個 int 物件占有的兩個或四個記憶體單元的首位址一樣。 

把 &foo 賦給乙個普通的指標是可以的,不過會觸發乙個 warning,因為int * 與 int (*)[1] 並不相容。賦值後普通指標的值與 &foo 的值是相同的,都是陣列物件的首位址,只是普通指標把這塊記憶體當做 int 物件處理而已。 

由於 c 語言是弱型別語言,你把 &foo 賦給int **********bar 或者 int *baz都是可以的,都不會導致 error,只會導致 warning,此時你列印出 *bar 或者 *baz 的值都是 foo 中第乙個整數的值(前提是指標和陣列占用空間大小相等)。正如文章開頭的**那樣,以這個整數的值作為乙個位址值進行間接訪問(*(*bar))就會導致非法訪問的錯誤。

陣列作為右值時會發生什麼?返回陣列物件內的所有值自然不可能,因此 c 語言中採取的方法是陣列作為右值時返回物件中元素型別的指標,指標指向第乙個元素,類似上乙個例子:

int main()

出錯資訊為:

error: incompatible types when assigning to

type

'int[1]' from type

'int *'

foo 作為右值時返回了乙個 int *,就是這個特性給人造成了陣列就是指標的假象。

陣列名和指標區別

前兩天同學問了我乙個問題 陣列名和指標有什麼區別,或者說這兩者有沒有區別?我回顧了下,大學課堂上老師講的,貌似記得老師說陣列名就相當於指標,比如 一維陣列就是一級指標 二維陣列是二級指標 之類的。結果同學告訴說原來我也是這麼理解的,其實 陣列名與指標有著本質的區別 然後給我推薦了一篇博文。我們經常見...

陣列名和指標的區別

魔幻陣列名 請看程式 本文程式在win32平台下編譯 1.include 2.int main int argc,char argv 3.size 3 1 陣列名不是指標 size 我們先來推翻 陣列名就是指標 的說法,用反證法。證明 陣列名不是指標 假設 陣列名是指標 則 pstr和str都是指標...

陣列名和指標的區別

引言 指標是c c 語言的特色,而陣列名與指標有太多的相似,甚至很多時候,陣列名可以作為指標使用。於是乎,很多程式設計者就被搞糊塗了。而許多的大 學老師,他們在c語言的教學過程中也錯誤得給學生講解 陣列名就是指標 很幸運,我的大學老師就是其中之一。時至今日,我日復一日地進行著c c 專案的開發,而身...