指標和陣列關係初探

2021-08-22 04:05:33 字數 4486 閱讀 7660

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

指標是c語言中的精髓。《高質量c++程式設計指南》的作者林銳就曾說過:不會正確使用指標,肯定算不上是合格的程式設計師。昨晚我思考了一宿,自認找到了理解指標的正確途徑。本文試圖通過**指標和陣列的關係去研究指標。

一般的c語言教科書上都會有這樣的話:指標就是位址,陣列名就是指標的首位址。這些不能不說是錯誤的,但是卻沒有深入進去,學生很難有較深的理解。

我認為從本質上看,陣列是乙個單獨的記憶體塊,指標是單獨乙個記憶體單元。這個原理也許誰都懂。理解指標和陣列,我覺得還要理解編譯器的執行原理。比如,簡單的如下面的幾行**,你知道編譯器做了哪些工作嗎?

floatx=123.456;

doublep=3.1415926;

printf("%7.2f/n",x);

printf("%7.2lf/n",p);

printf("%-7.2f/n",x);

printf("%-7.2lf/n",p);

上面幾行**,在vc中編譯往往會出現乙個警告:warning c4305: 'initializing' : truncation from 'const double' to 'float'。大意是將double型資料非法截斷為float型。這個警告是針對第一行的。也許你會說:123.456不是浮點型嗎?原因在哪兒呢?

這簡單的一行程式,對於編輯器來說,要做兩個步驟:一是開劈變數,二是斌值。然而,編譯器在這兩步前就已經做了一步:開劈常量。注意:你會說這個程式裡壓根兒沒有const。

但是,程式在這樣處理的:在開劈變數之前,首先把你的程式掃瞄一遍,凡是你輸入的數值、字元全部視為常量存放起來。所以,這麼簡單的一句話,不僅僅是開劈乙個變數,

在此之前早就開劈了乙個常量。它的型別是double,值為2.34。你所要做的,就是提醒譯器「這是float型」,你把這行改為:float x = <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2.34f;即可。現在你還看不到這個程式和我們要談的話題的關係,但等下你會看到的。

本文試圖通過一些例子來說明問題。首先我想請讀者思考下面的問題:

例子一是那個著名的字串複製程式:

#include

#include

voidstrcpy(char*strdin,char*strsrc)

}voidmain()

這個程式會有乙個錯誤。錯誤行為strdin++=strsrc++;編譯器會提示說:=左邊應是乙個左值。你會問:為什麼會有這個錯誤?我暫時不公布答案,我們繼續看例子。

這是乙個《高質量c++程式設計指南》裡的例子:

chara=「hello」;

a[0]=『x』;

cout

p[0]=『x』;

cout<

注意這個程式編譯是並沒有錯誤,但執行時會出現記憶體不能written的錯誤。為什麼?

例子三:

#include

intmain(void)

這個程式會有乙個錯誤:'=' : cannot convert from 'char [6]' to 'char [20]'。大意是=不能將char [6]轉化為char [20]。你可能會問:為什麼?

現在我開始公布我的思考成果。我覺得問題的本質是乙個常量與變數的問題,或者說是乙個許可權問題。為什麼strdin++=strsrc++;這句不能實現字元複製。其實我們把這行**分解一下就會明白.

strsrc++;

strdin++;

strdin= strsrc;

首先我們明確strdin和strsrc都是乙個字元指標變數,它有權改變放在自己的值。比如比如你可以給strdin和strsrc賦任何值。

問題是你有權改變自己,卻無權改變他人。假設strdin對應的值是1000,strdin對應的值是2000,

strsrc++; // 這時strsrc變為1002

strdin++; // 這時strdin變為2002

strdin= strsrc; //strdin==2002。

你想這樣有何不可呢?這樣是可以的。但是你不要忘記,strdin是乙個字元指標,也就是說strdin是乙個記憶體位址,當strdin==2002時,你想你有權力改變2002這個記憶體單元的值嗎?顯然這樣是無法複製字串的。

在例子2中你會看到char a變為「xhello」.但是字串p並沒有改變。你可能會為p抱不平:為什麼陣列a的字元可以改變,而指標p不能改變呢?這時因為「world」是乙個常量字串。char *p = 「world」; 這一句編譯器首先是找乙個記憶體塊把「world」這個常量字串存諸起來,然後把首位址賦給p。因為這是乙個常量字串,也就是說它的字元是不能改變的。你可以去讀這個字串,卻不能改動這個記憶體塊的任何內容。那為什麼陣列a可以改變呢?因為陣列a開闢的是乙個記憶體塊,它對這個記憶體塊既可讀又可寫。這時你可能會說:陣列可以轉化為用指標表示,而指標卻不一定能轉化為陣列。這種說法是有一定道理的。

在例子3,為什麼不能這樣給字元陣列a賦值呢?道理是一樣的。a="hello";只是把常量字串的首位址賦給a,但是陣列a的首位址是乙個常量,在定義時編譯器已經開闢了乙個空間給它。你可能會問:為什麼char a="hello";這時因為陣列a對開闢的這個記憶體塊擁有絕對的權力,既可以讀又可以寫。這好比某個人在城東有一塊土地,他可以在這塊土地上種任何莊稼,但他卻不能將這塊土地從城東搬到城西。

相對而言,一級指標還好理解點。二級指標就不一樣了。特別是二級指標和二維陣列之間的關係,更讓人捉摸不透。我覺得學習原理的最好方法做實驗。對程式而言就是想辦法編一些例程去驗證,從而得出一些結論。下面是我的乙個例程:

#include

voidprintfarray(int*parray)

printf("/n");

}intmain(void)

; int**pnum=new

int*[2];

inti=0;

intj=0;

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

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

}for(i=0;i<2;i++)

}printf("/n");

printfarray(a[0]);

printfarray(pnum[0]);

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

deletepnum;

pnum=null;

return1; }

執行結果是:

指標和陣列關係初探程式測試結果圖1

你可能奇怪:為什麼第3行和第2行的輸出結果怎麼不一樣呢?現在我們把它的位址輸出來,

把程式改為:

#include

voidprintfarray(int*parray)

}printf("/n");

}intmain(void)

; int**pnum=new

int*[2];

inti=0;

intj=0;

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

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

}for(i=0;i<2;i++)

}printf("/n");

printfarray(a[0]);

printfarray(pnum[0]);

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

deletepnum;

pnum=null;

return1; }

執行結果是:

指標和陣列關係初探程式測試結果圖2

#include

voidprintfarray(int*parray)

}printf("/n");

}intmain(void)

; int**pnum=new

int*[2];

inti=0;

intj=0;

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

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

}for(i=0;i<2;i++)

}printf("/n");

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

}printf("/n");

printfarray(a[0]);

printfarray(pnum[0]);

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

deletepnum;

pnum=null;

return1; }

執行結果為:

指標和陣列關係初探程式測試結果圖3

這下你該看到了吧,二級指標中行與行的位址並不是連續的,但在二維陣列中第二行首元素的位址僅接著第一行末元素的位址。這是因為使用new可以開闢一段連續的記憶體位址,但兩次new之間開闢的位址卻不是連續的。這樣你就明白了,為什麼把二級指標pnum傳進函式,卻只能把頭兩個元素輸出來。還有在二維陣列中a和a[0]是同乙個東西,都表示陣列的首位址,而pnum和pnum[0]並不是同一樣東西,pnum是pnum[0]的位址。不信,你可以把它們都輸出來看一下。

指標陣列和陣列指標之間關係

指標陣列 首先它是乙個陣列,陣列的元素都是指標,陣列佔多少個位元組由陣列本身決定。它是 儲存指標的陣列 的簡稱。陣列指標 首先它是乙個指標,它指向乙個陣列。在32 位系統下永遠是佔4 個位元組,至於它指向的陣列佔多少位元組,不知道。它是 指向陣列的指標 的簡稱。1 int ptr1 10 2 int...

指標和陣列的關係

指標和陣列的關係 參考朱有鵬c語言大講堂 1 指標和陣列的型別匹配的問題 int p int a 5 p a 型別的匹配的問題 p a a和a的數值是一樣的,但是 a時表示整個陣列的大小的 位址的列印是一樣的。a a a 0 從數值上看是完全一樣的,從意義上面看,a和 a是整個陣列的首位址,從型別來...

指標和陣列的關係

陣列名是乙個指標常量,表示陣列第乙個元素的的起始位址。如 int a 5 a表示陣列第乙個元素a 0 的起始位址 a 0 一 引用陣列元素的方法 用陣列下標引用陣列元素 陣列a中元素用下標表示為 a 0 a 1 a 2 a 3 a 4 用指標引用陣列元素 陣列a中元素用下標表示為 int p a p...