小議C語言中的陣列和指標

2021-08-03 04:36:56 字數 4293 閱讀 5742

1.引言

在c語言中,陣列和指標是其中非常重要又聯絡緊密的兩種資料型別,同時也是重點難點集中的地方。在學習這些內容時,經常會碰到這樣一些問題,例如:陣列名是什麼,就是首位址嗎?陣列如何訪問元素?陣列為什麼不能進行越界檢查?陣列表示法和指標表示法有何關係,誰更好?正確理解這些問題,對指標和陣列的使用是非常有幫助的。

2.陣列名的含義

在大多數的教材中都對陣列名作出這樣的解釋:陣列是一組資料的集合,它們在記憶體中佔據一片連續的儲存空間,陣列名並不代表整個陣列,而是陣列佔據的連續空間的起始位址[1]。例如:

int a[10], *p;

p=a;

在上面的語句直接將陣列名賦值給指標變數p,指標變數p就獲取了陣列a的首位址。在這裡a表示陣列的首位址,但並不是所有場合陣列名都是首位址。例如:

int a[10];

printf("%dn",sizeof(a));

printf("%dn",a);

printf("%dn",&a);

將程式補充完整執行後得到如下結果:

如果將陣列名理解為指標常量,sizeof(a)的值應該是4,因為所有型別的指標都只佔4個位元組。而實際執行的結果是40,sizeof(a)返回的是整個陣列所佔的位元組數。同樣,如果陣列名是指標常量的話,&a的值應該是指標的位址, &a和a應該是兩個不同的結果。而結果是陣列的首位址,&a和a結果相同。可以看出,在sizeof(a)和&a這兩種用法中,a都被理解為陣列本身而不是陣列的首位址。

所以,陣列名和指標常量並不等同,陣列具有確定數量的元素,陣列名除了是首位址外還包含了陣列長度、陣列型別等的屬性資訊。而指標是乙個標量,只有乙個表示位址的值。當陣列名出現在表示式(sizeof和&除外)中,編譯器會其生成乙個指標常量。因此,應該說陣列名的值是指標常量,而不能說陣列名就是指標常量[2]。

3.字串常量和指標

字串常量在記憶體中佔據一塊連續的儲存空間,不能當作乙個簡單的標量,這點和陣列是非常相似的。如果表示式中出現字串常量該如何理解呢?既然陣列名的值是陣列的首位址,那麼字串常量是否也有類似的用法呢?我們知道字串常量可以賦值給字元指標變數,例如:

char *ptr="hello";

"hello"是個字串常量,通過上面的操作指標ptr獲取了該字串在記憶體中的首位址,這裡字串常量理解為其首位址。如果字串"hello"出現在其他地方是否也可以同樣理解呢?請看下面的程式。

printf("%sn","abc"+1);

printf("%cn",*"abc");

printf("%cn","abc"[2]);

printf("%cn",*("abc"+2));

將程式補充完整執行後結果如下:

結果跟預料的一樣,在上面的表示式中,字串常量的值也是該字串的首位址。"abc"+1的結果是乙個指向字元b的指標;*"abc"表示首位址指向的乙個字元;"abc"[2]和*("abc"+2)等價,表示將首位址加2,然後取出新位址指向的乙個字元。這和我們對陣列名的使用方式類似,字串常量的值是該字串的首位址。字串常量的這種用法可讀性較差,但在某些場合下使用會有很好的效果。例如下面的函式實現將10進製數轉換為16進製制數。

btoascii(unsigned int num)

由於字元0到9和字元a到f的ascii碼並不連續,所以上面的程式中使用了if分支來對取餘後的結果分別處理轉換成相應的16進製制字母。如果使用字串常量則比較簡單。改寫後的函式如下:

btoascii(unsigned int num)

4.陣列如何訪問元素

在c語言中,陣列訪問元素的[ ]符號被解釋為下標運算子。在大多數的教材中是這樣解釋的:在計算整型a[i]時,先將整型的位元組數4×i,再加到首位址a上得到元素位址,然後通過位址取出該元素的值,即a[i]和*(a+i)等價。確實,從兩種寫法編譯後的彙編語句來看是完全相同,但這只能證明兩者是等價的,但並不能說明a[i]就是按*(a+i)編譯處理的。那麼有沒有什麼方法可以驗證a[i]就是按*(a+i)編譯處理的呢?

如果下標表示法是按指標表示法編譯執行的,那麼可以設想2[a]的寫法也應該是對的,因為它可以解釋為*(2+a),同樣可以訪問陣列的第3個元素。程式設計如下:

int a[10]=;

printf("%dn",2[a]);

將程式補充完整後執行結果為:

可以看出2[a]也是可以編譯通過的寫法,並且就等於a[2],這只有按指標法來理解才說的通,否則它是不符合c語言裡關於識別符號的命名規則的。理解了下標法的編譯處理方式,也就不難理解為什麼c語言中不對陣列進行越界檢查了。因為陣列訪問元素的下標運算實際是變址運算,與指標表示法是等同的,所以即使下標值超過了陣列的長度,也是可以計算位址並訪問資料的,只不過訪問到的資料是不確定的。

5.下標法和指標法的效率比較

既然下標法和指標法是等價的,那麼哪種表示法的效率更高呢?在很多教材裡都提到指標的**效率高,但它究竟是如何做到的呢,我們試著來分析一下。例如:將陣列元素依次賦值為0,下標法**如下:

int a[10],i;

for(i=0;i<10;i++)

a[i]=0;

在vc6.0中編寫這段**並檢視對應的組合語言結果如下:

00401028 mov 

dword ptr [ebp-2ch],0

0040102f jmp 

main+2ah (0040103a)

00401031 mov 

eax,dword ptr [ebp-2ch] 

00401034 add 

eax,1

00401037 mov 

dword ptr [ebp-2ch],eax 

0040103a cmp 

dword ptr [ebp-2ch],0ah

0040103e jge 

main+3dh (0040104d)

00401040 mov 

ecx,dword ptr [ebp-2ch] 

00401043 mov 

dword ptr [ebp+ecx*4-28h],0 

0040104b jmp 

main+21h (00401031)

將源**改寫成指標的形式:

int a[10],*p;

for(p=a;p eax,[ebp-28h]

0040102b mov  dword ptr [ebp-2ch],eax

0040102e jmp  main+29h (00401039)

00401030 mov  ecx,dword ptr [ebp-2ch]

00401033 add  ecx,4

00401036 mov  dword ptr [ebp-2ch],ecx

00401039 lea  edx,[ebp]

0040103c cmp  dword ptr [ebp-2ch],edx

0040103f jae  main+3ch (0040104c)

00401041 mov  eax,dword ptr [ebp-2ch]

00401044 mov  dword ptr [eax],0

0040104a jmp  main+20h (00401030)

比較這兩段彙編**,可以看到對a[i]的求值需要先取得i的值,並將i與4(整型的長度)相乘然後從位址中取值,而乘法是要花費一定的時間和空間的。指標表示法中,p++的實現是直接將p的值加4,沒有用到乘法。顯然這兩段**,第2種使用了指標表示的**執行效率較高。

從上面的舉例可以看出,當要讓陣列1次1步的移動時,指標比下標要更有效率。但要注意僅僅將下標法改寫成指標法是沒有任何區別的。例如:

int a[10],i; 

int a[10],i;

for(i=0;i<10;i++) 

for(i=0;i<10;i++)

a[i]=0; 

*(a+i)=0;

這兩段**產生的編譯**完全一樣。指標的高效率體現在將下標變址語言中的乘法轉換成了指標變數的加法,實現這種轉換的**才會獲得效率的提高。對程式而言,可讀性對**的維護也是非常重要的,這點下標法要比指標法好。因此,在對可讀性影響不大的前提下,可選擇用指標法代替下標法。

6.小結

陣列和指標是c語言中頻率很高的兩種資料型別,其中涉及到的語法內容很多。本文主要對教材中較少涉及或未深入分析的一些語法現象(例如,陣列名的含義、陣列如何訪問元素、下標法和指標法的效率比較)進行了分析研究。通過這樣的分析,可以幫助理解陣列和指標兩者之間的一些語法關聯,並促使學習者對c語言中更多關聯語法現象進行思考,更好地掌握c語言的語法規則。

C語言中陣列指標和指標陣列

如果看這兩個概念的英文翻譯可能會更容易理解 陣列指標 pointer to array 指標陣列 a array of pointer 顧名思義,陣列指標就是乙個指標變數,該變數指向乙個陣列。而指標陣列就是乙個陣列,這個陣列的元素都是指標,即每乙個元素都可以當成乙個指標變數使用。下面給出兩者的定義和...

C語言中的陣列 指標陣列 陣列指標

1 include 2 3 int main 4 執行結果 0xbfeefda9 0xbfeefdaa 0xbfeefdab12 70xbfeefda9 0xbfeefdaa 0xbfeefdab12 7以上執行結果可以得出如下結論 char str 3 1 str i strr i 2 str i...

C語言中的陣列指標

一 c語言中的陣列有自己特定的型別 陣列的型別由元素型別和陣列大小共同決定 例如 int array 5 的型別為 int 5 二 定義陣列型別 c語言中通過typedef為陣列型別重新命名 typedef type name size 陣列型別 typedef int aint5 5 typede...