陣列、函式與指標 - 程式人生

文章推薦指數: 80 %
投票人數:10人

參考. The C Programming Language-Chapter 5 Pointers and Arrays. 前言. 在上一篇文章動態陣列(一維二維)探祕介紹了陣列的一些知識,在最後碰到了 ... 程式人生>>陣列、函式與指標 陣列、函式與指標 阿新••發佈:2020-05-18 參考 TheCProgrammingLanguage-Chapter5PointersandArrays 前言 在上一篇文章動態陣列(一維二維)探祕介紹了陣列的一些知識,在最後碰到了一個如何申請二位陣列的問題,這篇文章就延伸一下,介紹介紹陣列、函式和指標更深層次的關係。

基礎知識 inta[10] 一維陣列,陣列中有連續的十個元素,每個元素都是int型別。

int*p 指標,儲存的是一塊資料的地址,這塊資料是int型別,也就是當程式訪問到p指向的地址的時候,需要按照int型別把後面連續的幾塊資料按照一個整體讀取 intv int型別的資料 p=&v 把v的地址賦值給p,那麼現在p指向的就是v的地址 p=&a[0] 把陣列第一個元素的地址賦值給p,那麼現在p指向的就是陣列a第一個元素的地址 int*p到底是什麼 按照TheCProgrammingLanguage中介紹,這個表示式應該看成int(*p),也就是*p是個變數,它是一個int型別,與intv是等價的。

*在變數前表示把當前的指標型別解析成它指向的資料型別,那麼去掉*,就表示它是一個指標。

進一步說,就是,p是一個指標,*的作用是把p(指標)解析成它指向的資料,*p就是p指向的資料,型別是int,也就是我們說的p是一個指向int型別的指標 如果這樣理解的話,下面這條宣告變數的語句就很好理解了 int*p,v; 由於運算子優先順序的關係,這個等價於 int*p;intv; *p和v是等價的。

這條語句相當於聲明瞭兩個int變數,*p和v。

v無法進一步解析,所以v就是一個int變數;*p可以進一步解析,把*去掉,那麼p就是一個指標,也就是指向int型別變數的指標。

inta[10]中的a又如何理解 由TheCProgrammingLanguage中我們可以看到,a指向了a[10]陣列開始的地址,與&a[0](陣列第一個元素的地址)是一樣的,如下圖 設定p等於a或是a[0]的地址,這樣p和a的作用就一樣了。

只不過a是一個常量,p是一個變數,a不能被賦值或是做加減乘除的運算,p可以 p=a; //或者 p=&a[0]; 陣列與指標有著密切聯絡,陣列可以看做指標指向了一塊連續的記憶體區域,實際上也確實如此。

如下圖 a[0]就是*(a+0)也就是*(pa+0),從這裡就可以理解為什麼c/c++的索引是從0開始。

在c/c++中,對指標做加一的操作,相當於把指標移動到下一個資料的位置,移動多少,取決於指標指向的型別是什麼,跨度就是這個型別佔用的空間。

比如上圖,不管a是什麼型別的陣列,a+1或是p+1,移動的距離就是a陣列中元素佔用記憶體的位元組數。

比如a中是char(佔用一個位元組),a指向0x0001,那麼a+1就指向0x0002;如果是int(佔用四個位元組),a指向0x0001,a+1就指向0x0005。

再談二維陣列 如下圖陣列a是一個二維陣列,相當於一個數組指標的陣列,這個陣列有2個元素,每個元素又指向了一個一維陣列,每個一維陣列的元素個數是5 那麼二維陣列的指標是什麼呢?我們知道一維陣列相當於指標,那麼二維陣列相當於陣列中儲存了一維陣列,也就是陣列中儲存了指標,也就是指標陣列,那麼二維陣列就相當於指標的指標。

但是當我們編譯下面的程式碼的時候,會提示errorC2440:'initializing':cannotconvertfrom'char[2][5]'to'char**',滑鼠放在出錯的地方提示avalueoftype"char(*)[5]"cannotbeusedtoinitializeanentityoftype"char**" chara[2][5]; char**p=a; 這是為什麼呢?實際上二維陣列,或是多維陣列的指標不是這樣定義的,必須要指定這個陣列指標陣列中每個元素是什麼樣的,如下才是正常的 chara[2][5]; char(*p)[5]=a; 實際上我們可以用一個指標操作二維陣列,也可以用指標的指標操作二維陣列,只需要強轉就行。

不管是幾維的陣列,在記憶體中都是連續的,我們只需指向陣列的開始位置,一個個訪問就可以了。

chara1[2] char*a2[2] char(*a3)[5] chara4[2][5]傻傻分不清楚 在動態陣列(一維二維)探祕中我們碰到了一個問題,就是,從記憶體中看,申請一個一維陣列和二維陣列一樣,都是一塊連續的空間,但是如果用一維陣列的方式申請一塊連續的空間,我們用指標加1,發現它並不會像真正的二維陣列一樣,向前跳動一排的元素空間大小,還是跳動一個元素大小。

如下示例 chara[2][5]; char*pa=newchar[2*5](); a的地址是0x004CFE00 a+1的地址是0x004CFE05 pa的地址是0x00227D68 pa+1的地址是0x00227D68 這是為什麼呢?很明顯,pa不管怎麼操作,它只是一個char的指標,那麼按照c語言的規則,每次跳動只能是一個char的大小,也就是1;而a是一個char[5]的指標,那麼每次跳轉就是char[5]的大小,也就是5. 上面的4個,有一個是特殊的,就是char(*a3)[5]。

其他的都是陣列,這個是指標: chara1[2]是一個一維陣列,裡面有兩個char元素 char*a2[2]是一個數組,裡面有兩個char*元素 chara4[2][5]是一個二維陣列,裡面有2*5個char元素,或是說裡面有2個char[5] 而char(*a3)[5]是一個指標,指向的型別是char[5]。

我們可以用上面的方式拆分一下,char(*a3)[5]是一個數組,裡面儲存的是有5個char的連續資料,*a3就是這個陣列,去掉*,那麼a3就是一個指標,指向的是一個char[5] chara1[5]chara2[2][5]char(*a3)[5]有什麼聯絡 chara1[5]和chara2[2][5]的指標形式都是char(*a3)[5],這就是它們之間的聯絡。

可能看上去有點懵,實際上這個與charb1和charb2[5]的指標形式都是char*b3是一樣的。

我們知道charb1和charb2[5]的指標形式都是char*b3,如果b3=b1,那麼b3就指向了b1的地址;如果b3=b2,那麼b3就指向b2第一個元素的地址,b3++就可以訪問b2的第二個元素。

在c語言中,沒有越界檢測,這即提供了方便,也增加了風險。

c語言中最危險的陷阱之一就是越界,也就是野指標。

從邏輯或是實現上來說,這又是c語言中的精髓,底層邏輯簡單,應用執行速度快,不需要考慮任何額外的操作。

如果是指標,那麼指標加一,就是跳轉到下一塊資料,也就是把當前指向的這塊資料跳過去。

同樣a3=a1,就是指向了一塊資料,這個資料型別是一個char[5]。

如果是多個char[5]呢?比如a3=a2,那就是一個一行5個元素或是說5列的二維陣列了。

a3++,就是跳轉5個元素的大小,這樣就可以直接用a3[1][2]的方式訪問了,這就是二維陣列。

函式與指標 在c語言中,函式並不是一個變數,但是可以定義成一個指標,允許被賦值、傳遞等。

intfun1(int*a,int*b); int*fun2(int*a,int*b); int(*fun3)(int*a,int*b); int*(*fun4)(int*a,int*b); fun1是一個函式,函式的返回值是int,函式有兩個引數,每個引數都是int指標 fun2是一個函式,函式的返回值是int指標,函式有兩個引數,每個引數都是int指標 fun3是一個指標,指標的型別是一個函式,這個函式的返回值是int,函式有兩個引數,每個引數都是int指標 fun4是一個指標,指標的型別是一個函式,這個函式的返回值是int指標,函式有兩個引數,每個引數都是int指標 我們可以看出fun3就是fun1的指標,fun4就是fun2的指標。

fun3=fun1; fun4=fun2; 做了以上賦值後,呼叫fun3就相當於呼叫fun1,同理呼叫fun4就相當於呼叫fun2。

呼叫方法如下 inta=1; intb=2; intret=0; int*pret=nullptr; ret=fun1(&a,&b); ret=(*fun3)(&a,&b); pret=fun2(&a,&b); pret=(*fun4)(&a,&b); 在這裡我們看到好多用法定義都與陣列和陣列的指標類似。

同樣fun3與fun1的區別,fun1和fun2是常量,不可以修改賦值,而fun3和fun4可以。

函式指標強轉 雖然這是一個小知識點,但是可以幫助我們進一步瞭解指標,比如我們有一個函式需要傳入函式指標,引數是void指標,需要我們把int指標引數的函式強轉傳入 inttestcomp(inta,intb) { returna; } inttestcomp1(longa,longb) { returnb; }voidtest(int(*comp)(inta,intb)){   inta=(*comp)(111,222);   cout<



請為這篇文章評分?