天天看點

手繪知識點——數組指針

各位老鐵國慶節過的可還好,前天看了大閱兵,很激動,國富民強,我等也應該感到自豪~

來到指針系列第四篇,我們聊聊數組指針,即指向數組的指針。那您可能會問了,這東西有啥好說的,前邊不都提了嘛,最多就是指針的加加減減,取個元素值啥的,其他也沒什麼可說的啊。哎(二聲),事實勝于雄辯,咱們還是走一波~

首先大家應該回顧一下數組是個啥東西,按照官方的說法數組是一系列具有相同類型的資料的集合,劃重點,一系列----相同類型,我們需要注意的是數組元素在記憶體中是連續存放的,差別于咱們前邊提到的那些連續定義的變量,他們中間還有一些輔助的其他變量,而數組各個數組元素之間是緊密相連的,另外就是同一數組内所有元素的類型都是一樣的,這樣便于管理和各種操作。

為了精簡文本就直接上代碼吧,前兩篇的博文文字太多,我自己看着都眼暈,這次我們盡量長話短話且說的直白接地氣:

#include <stdio.h>

int main(){
	int arr[] = { 1, 10, 23, 101, 298, 376 };
	int *a = arr;
	int len = sizeof(arr) / sizeof(arr[0]);	
	for (int i = 0; i<len; i++) {
		printf("%d  ", arr[i]);
		printf("%d  ", a[i]);
		printf("%d  ", *(arr + i));
		printf("%d  ", *(a + i));
		printf("%#x\n", arr + i);
	}
	printf("%#x,%#x,%#x,%#x\n", arr, a, &arr[0], &arr);
	printf("%#x,%#x\n", arr + 1, &arr + 1);
	printf("\n");
	return 0;
}
           

我們定義了一個整型數組arr,并初始化了六個值,然後定義了數組指針a,它和arr一樣都指向數組的第一個元素,即“1”。(這裡大家注意數組名arr嚴格來說并不是指針,它是一個常量,其值為數組首元素的位址,是以數組名是不能進行++和--的,這裡我我們為了友善暫且認為數組名就是一個指針,如果大家感興趣可以去論壇搜一下這塊,讨論的還是很激烈的);

接着往後的sizeof(arr)得到的是數組arr占據的記憶體大小,即整個數組所有元素共占有多少位元組,而sizeof(arr[0])得到的是一個數組元素占據的記憶體大小,二者的商即數組的元素個數(這裡我嘗試用了strlen(),結果為1,這一點後續談論字元串時再細聊);

再往後就是一系列的輸出了,我們先看一下結果:

手繪知識點——數組指針

在記憶體中的具體存儲如下圖所示:

手繪知識點——數組指針

由結果可知,我們輸出數組元素可以使用下标,如arr[0]、a[0],也可以使用指針,如*(arr+1)、*(a+1),他們輸出的結果是一樣的

另外一點就是倒數第二行的輸出,四個值都是一樣的,大家再看代碼中的四個輸出值,前三個arr , a , &arr[0]很好了解,都指向數組首元素,那最後一個&arr怎麼也是這個值呢,大家都知道,如果arr是指針的話,那取其位址所得到就應該是二級指針(這個我們後邊會細聊 , 可以了解為指向指針的指針),但輸出結果告訴我們,這并非是一個二級指針,二者指向的都是數組首元素,那它倆有啥差別呢?

來看最後一行的輸出,我們分别讓這哥倆(從形态上來看應該是父子倆)分别往後走一步,看看邁出的步伐是否整齊,結果顯而易見,二者腿并不一樣長,一個走了四個位元組,指向了下一個數組元素,另一個直接一步跨出了整個數組,并且從位址來看恰巧是數組末尾元素的下一個位置(如手工畫圖所示),到這裡大家應該就明白了,arr+1相當于從數組首元素開始前進一個數組元素大小的距離,而&arr+1則從數組首元素開始前進一個數組大小的距離,可以簡單了解成 : 前者的arr代表數組首元素的位址,後者的&arr代表整個數組的位址(還可以這麼想 , 我們都知道arr是常量 , 那麼它就不能取位址了 , 既然可以輸出&arr并沒有報錯 , 那麼此時的arr已經不是之前的arr了 , 這是典型的一詞多義啊),咋樣,是不是已經淩亂了,有沒有一種自由飛翔的感覺~~~

還有比較重要的一個易混點就是數組指針的取值和自增自減的聯合運用,我們輸出一下數組指針a的幾個值:

printf("%d\n", *a);
printf("%d,%d,%d,%d,%d\n", *a,*a++, *++a, (*a)++, ++*a);
           

大家可以先自己算一下,你們認為正确的輸出應該是怎樣的~~~

看結果:

手繪知識點——數組指針

如何,是不是和你們算得不一樣……這一點大家不要糾結了,我的結果也和它不一樣 , 這一塊我查了很多資料,百思不得其解,很多人都說printf函數的操作原理是從右向左計算,然後從左向右輸出,按無論怎能來都不能解釋這個結果,最後在論壇看到一位朋友的解釋,感覺很有道理,直接截圖了:

手繪知識點——數組指針

綜上也就是說罪魁禍首就是編譯器,它們的解析不盡相同,進而導緻輸出結果的不同。。。

最後再說幾點吧:

1.我們定義數組指針是為了友善對數組元素進行一系類操作,畢竟數組名是個常量,沒有變量來的友善;

2.計算數組大小時不可用sizeof(a),我們之前說過sizeof(xx)得到的是xx本身的大小,與其指向的類型是沒有關系的,那這個結果就不準确了(一般指針都占據4位元組大小)

3.我們定義數組指針時可以直接這樣定義:int   arr[]={XXX,XXX},*a=arr ; 這樣避免犯之前的錯誤,使得數組指針指向的類型與數組元素類型不一緻(如int   arr[]={XXX,XXX} ;    double  *a=arr ;)

4.上邊提到的sizeof , ++ 和*都是操作符 , 了解其優先級有助于對具體操作的了解 , 也為後續的讨論打下堅實的基礎 :

手繪知識點——數組指針

—如果本篇内容對你有一點點幫助,請點個贊或者收藏關注一下,讓我們一起努力—

——————————————————   分割線   —————————————————————

2019.10.14更新

最近幾篇手繪知識點--指針系列文章閱讀量差異較大,比如第二篇耗費大量精力最終木有過百,心涼啊。。。

剛建立了一個公衆号,一開始的文章是和csdn部落格同步的,後續會着重來做這一塊,比如釋出第一手資訊,抽個獎啥的,希望各位小夥伴支援一下,加個關注,如果能幫忙宣傳一下就更完美了,愛你們,還是那句話,讓我們一起努力,共同進步~

公衆号為“非著名IT表演藝術家”,比較中二的名字,就是靈光一閃,然後這個名字就冒出來了……

大家也可以掃碼關注,拜托了:

手繪知識點——數組指針

繼續閱讀