C語言 指針變量及相關的知識

指針是一個特殊的變量,因爲他的運算方式和所代表的東西跟基礎數據類型不一樣。指針變量存儲的是內存單元的地址編號,這地址就跟我們家門口的門牌號一樣。當用解引用運算符(*)的時候,CUP就會到對應的內存單元取內容,至於取多少,主要看定義時定義的是什麼類型的指針,也可以用下標運算符([])。

基礎知識

1.指針變量佔用的字節大小都是一樣的。

在相同位數的系統上,不同的指針變量佔用的字節大小都是一樣的,但會隨着系統位數的增加而增加。因爲指針指向的是內存地址,比如說32位系統上,最大能支持的內存單元個數是2^32,所以指針佔用的字節數只需要4個字節(4*8=32)就能訪問到系統上任意位置上的內存單元了。64位的指針就是8個字節如此類推。別誤解了上說的32位系統就只是系統,同樣也是32位軟件。因爲系統是向下兼容的,即使你是64位系統運行32位軟件,指針也只是4個字節。
2.解引用 * 和下標運算符[]

解引用
"*"的作用是引用指針指向的變量值,引用其實就是引用該變量的地址,“解”就是把該地址對應的東西解開,解出來,就像打開一個包裹一樣,那就是該變量的值了,所以稱爲“解引用”。也就是說,解引用是返回內存地址中保存的值。例:

	int i = 0x12345678;
	int *pi = &i;//等同於int *pi;   pi = &i;
	printf("&i = %x, i = %d, *pi = %d", &i, i, *pi);

在這裏插入圖片描述
在這裏插入圖片描述

分析:*pi其實是將pi的值0x1cfd48當作地址,然後到0x1cfd48內存地址上去取內容,由於pi是int *類型,那就按照int的方式去取,把0x1cfd48開始的四個字節內容取出來。如果把pi的類型改成short *,那麼他只會取出0x5678。由於這是大端模式,所以可以看到數據是高位在前的。

n級指針
多少級指針,就看定義的時候有多少個*了。多級指針很少用,一般就只用到二級指針(**)。解多級指針只不過是在多個地址間轉來轉去,最後到達目的地取內容,解多級指針的時候要注意,每解一次取得內容都是地址,直到解到了第n次纔是真實內容。

	int i = 0x12345678;
	int *pi = &i;
	int **ppi = π//等同於int **ppi;   ppi = π
	printf("&i = %x, &pi = %x", &i, &pi);
	printf("\nppi = %x, *ppi = %x, **ppi = 0x%x\n", ppi, *ppi, **ppi);

在這裏插入圖片描述
分析:ppi是二級指針,pi是一級指針,i是int變量。很明顯看出,多級指針是用於保存低一級的指針變量的地址。ppi保存pi的地址,pi保存i的地址。要想從ppi去取出i的內容,就必須是先*ppi(到pi地址上取出pi的內容),再 *把從pi取出的內容當作地址去取內容(到i的地址上取出i的內容),所以這就是**ppi執行的過程。

//下面代碼一樣可以取出i的內容。通過把二級指針轉成一級指針(從n級指針一級一級的往下轉)
	int i = 0x12345678;
	int *pi = &i;
	int **ppi = π
	int *temp = *ppi;//等同於int *temp;   temp= *ppi;間接等同於int *temp;   temp= &i;
	printf("%d", *temp);

下標運算符[]
參考數組方面的知識。後期再補上數組的文章。

3. 指針的加減法運算
指針只有加減法算術運算,並沒有乘除等算術運算,因爲指針的算術運算是根據指針類型去運算,並不是普通的算術加減。
減法
可以理解成 兩個地址之間的差值除去運算符左邊的“類型”的字節數的結果向上取整。例:

	char c;
	char *pc = &c;//char *類型,保存char c的地址
	//char *pc = 120;//可以替換掉上面兩行代碼,由於我並沒有對這個地址做任何操作,所以並不會出現內存訪問衝突錯誤,其他情況並不建議這麼寫,除非是要操作硬件
	unsigned int pc_value = pc;//將pc的值賦給pc_value,方便計算相差幾個字節
	int i;
	int *pi = &i;//int *類型,保存int i的地址
	//int *pi = 520;//可以替換掉上面兩行代碼
	unsigned int pi_value = pi;//將pi的值賦給pi_value,方便計算相差幾個字節
	//pi - pc,由於pi是int *類型的,所以是按照int的字節去計算,實際效果等同於(pi_value - pc_value)*1.0/sizeof(int)的結果向上取整。其他類型也如此。
	printf("&c = %x, &i = %x\n&i - &c = %d\n&c - &i = %d\n%d", 
		pc, pi, pi - pc, pc - pi, pc_value - pi_value);

在這裏插入圖片描述

分析:0xeffcd7 - 0xeffcb0 = -39。也就是兩個地址之間相差了39個字節。按四個字節來分,就是相差了10個這樣的空間。按一個字節來分就是相差了39個。

加法
根據數學的逆運算,可以將減法變換成加法,(地址-地址=相差個數)變成(地址+相差個數=地址)。具體例子由你們自行去驗證。

4.指針數組和數組指針
這兩個有點繞,一不小心就容易弄錯。其實從名字上來分的話,重點是後面兩個字,指針數組的數組,數組指針的指針。
數組指針
就是指向一個數組的指針,通常都是用於指向二維以上的數組。以指向二維數組爲例,定義格式一般爲

類型 (*變量名)[數組個數]

	int a[2][10] = { { 0,1,2,3,4,5,6,7,8,9 }, {10,11,12} };
	//數組指針
	int(*pArray)[10];
	pArray = a;//只需要初始化一次就能訪問二維數組a的任何元素了
	//pArray[0] = a;//錯誤賦值方式,錯誤信息是必須可修改左值。因爲這不是一個數組,所以沒辦法這樣賦值。
	//int (*pArray)[10] = a;//等同上面兩行代碼,把a替換成a[0]和&a[0]效果一樣
	printf("&a[0][0] = %x, &a[1][0] = %x\npArray[0] = %x, pArray[1] = %x\n",
		&a[0][0], &a[1][0], pArray[0], pArray[1]);
	printf("a[0][0] = %d, a[1][0] = %d\npArray[0][0] = %d, pArray[1][0] = %d",
		a[0][0], a[1][0], *pArray[0], pArray[1][0]);
	//(*pArray)[0]等同於pArray[0][0],(*(pArray+1))[0]等同於pArray[1][0]
	//*(*pArray)等同於pArray[0][0],*(*(pArray+1))等同於pArray[1][0]
	/*************************************/
	分析:1、定義一個指向數組長度爲10的一維數組的指針pArray,指針pArray等價於一個二維數組。
		  2、從上面可以看出,數組指針用法其實就跟n維數組差不多。只是把n維數組的最高維換成了(*pArray)。
		  3、數組指針的地址偏移量其實是數組佔用的內存大小,也就是10*sizeof(int),所以pArray[0]和pArray[1]相差了40。
	/*************************************/

在這裏插入圖片描述
在這裏插入圖片描述

指針數組
一個保存指針的數組。也就是數組裏面的值都是地址。定義格式一般爲

類型 *變量名[數組個數]

	int a[2][10] = { { 0,1,2,3,4,5,6,7,8,9 }, {10,11,12} };
	//指針數組
	//int *pArray2[10] = a;//指針數組的錯誤賦值方式,因爲這是數組,初始化方式是{}
	int *pArray2[10];//數組的元素必須要初始化,否則可能會出現操作非法內存的錯誤
	//pArray2 = a;//錯誤的賦值方式,因爲數組變量的變量名代表的是一個不可修改的值(等同於* const),只能修改數組的內容
	pArray2[0] = a;
	pArray2[1] = a[0];
	pArray2[2] = &a[0][0];
	pArray2[3] = a[1];
	pArray2[4] = &a[1][0];
	pArray2[5] = &a[1][2];
	int i = 100;
	pArray2[6] = &i;
	printf("a = %x, a[0] = %x, &a[0][0] = %x\n", a, a[0], &a[0][0]);
	printf("*pArray2[0] = %d, *pArray2[1] = %d, *pArray2[2] = %d\n", *pArray2[0], *pArray2[1], *pArray2[2]);
	printf("*pArray2[3] = %d, *pArray2[4] = %d, *pArray2[5] = %d\n", *pArray2[3], *pArray2[4], *pArray2[5]);
	printf("*pArray2[6] = %d\n", *pArray2[6]);

在這裏插入圖片描述
在這裏插入圖片描述
數組指針和指針數組加減法
這其實跟普通指針的加減法沒什麼區別,只是數組指針的字節大小是按數組的去計算而已。其他的並沒有什麼區別。大家自行驗證。

上面有哪裏說得不好的,不懂的歡迎大家提。大家一起來完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章