陣列、函式與指標 - 程式人生
文章推薦指數: 80 %
參考. 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<
延伸文章資訊
- 1指標函式與函式指標陣列— Function Pointers, Array of ...
前面我們知道指標可以指向任何的記憶體位址,這也代表指標可以指向任何一段可執行的程式碼,這也稱作為指標函式。 指標函式的宣告:(*代表指標的意思).
- 2將陣列(含多維)傳入副函式- CYCU-EE-C
printf(“%d”, A[0] ); 這樣輸出的答案是2,這樣大概了解陣列等於指標的事情了吧! 所以到底該怎麼傳入副程式 ...
- 3【C語言觀念複習筆記】函數指標陣列(Array of function pointer)
之前寫過一篇文章討論過指標陣列的觀念這個指標陣列的指標當然也是有型態的, 上述文章是char 而也可以是指向function 因為指標陣列在上面文章討論過了 ...
- 4陣列指標與函數
- 5函式指標 - OpenHome.cc
在參數的型態宣告複雜時,雖然不能使用 auto , decltype 的運用可以稍微緩解一下型態宣告的負擔。 也可以宣告函式指標陣列,例如: bool (*compare[10])( ...