C---指針&數組名&數組地址&變量對應的加減法---圖解篇

最終學習C/C++,遇到很多疑難雜症。希望用自己的理解和圖形化的方式,把核心的問題表達清楚,即使是小白,也能看明白什麼原理!(不忘初心,晚上加雞腿!)

在這裏插入圖片描述

  • pa是指針,指向了數組a,pa對應的是首元素的地址
  • 數組a有10個元素,都初始化好了
  • int類型的數組,每個元素佔用4個字節的大小,內存中每4個字節內存存儲了一個元素,共計40個字節大小,右邊暫時省略了!
  • 數組名a的值是首元素的地址a[0]的值,雖然a和a[0]表達的不一樣,但是值都一樣的

1.指針與整數的加減(指針的偏移)

指針的偏移,其實是地址的偏移,所以無論加減,其實本質都是地址位置的移動,*p + 1 的本質是 *(p+1),因爲先移動的位置,然後再去取值。所以 *(p+1)也更容易理解

#include <stdio.h>

int main() {

	int* pa;
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	pa = &a;					// 將指向指針數組a,其實默認指向a[0]這個元素
	printf("%d  \n", *pa);
	printf("%d  \n", *(pa + 1));	// 打印數組後面一個元素的值(內存中的概念是:從當前指針所指向的內存單元開始,往後拖動4個字節,然後再取4個字節的值,最終算出這個元素的值)
	printf("%d  \n", *(pa + 2));	// 同理,往指針後面移動8個字節之後,再取連續4個字節的內存,算出的值
	printf("================\n");
	pa = &a[2];  				// 將指針指向數組的第三個元素,a[2]
	printf("%d  \n", *pa);
	printf("%d  \n", *(pa + 1));	// 
	printf("%d  \n", *(pa + 2));
	return 0;
};


如上代碼中:pa = &a[2]; 其實就是把指針指向了第三個元素(索引爲2),入下圖所示

在這裏插入圖片描述


1
2
3
================
3
4
5

如上可以說明:

  • 指針指向的數組,其實就是把指針指向數組默認的首元素,通過指針的偏移,就可以找到最終每個元素的值了。(也可以通過a[i]這種方式獲取值的大小,但這不是我們將要說明的重點)
  • 指針既然可以指向第一個元素,也可以指向其他元素,那該如何獲取前面的值呢?

指針與整數的減法?

int main() {

	int* pa;
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	pa = &a[2];
	printf("%d  \n", *pa);
	printf("%d  \n", *(pa + 1));
	printf("%d  \n", *(pa + 2));
	printf("=================\n");
	printf("%d  \n", *(pa - 1));				// 左偏移1個元素
	printf("%d  \n", *(pa - 2));				// 左偏移2個元素
	return 0;
};


在這裏插入圖片描述

3
4
5
=================
2
1

總結:

  • 指針指向的數組地址,可以通過 pa = &a[i]形式修改,指向數組a的第i+1個元素
  • p+1,相當於把指針右移1個元素




2.數組名與整數的加減

#include <stdio.h>

int main() {

	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	printf("%d  \n", *a);
	printf("%d  \n", *(a + 1));		// 數組名a代表的是首元素的地址
	printf("%d  \n", *(a + 2));
	return 0;
};

1
2
3

通過如上發現:

  • 1.數組名的加法,與指針變量的加法類似,a + 1 與 p + 1 都是右移一個元素,運算完再用 * 取值,最終就可以獲取到元素的值了!
  • 2.注意,數組名a代表的是首元素的地址,&a可以代表 數組的地址。不同點在於,a的長度是sizeof(int)的字節長度,&a代表的是sizeof(a)的字節長度。




3.數組名轉換爲整數,再與整數的加減(數組名轉換後的計算)

int main() {

	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* pa = (int*)((int)a + 1);
	printf("%x  \n", *pa);

	return 0;
};

編譯環境爲32位,一般默認爲小端存儲方式。

  • 即數組的低位,存儲在內存的低位置;數組的高位存儲在內存的高位置。
  • 首元素1,對應的二進制爲00 00 00 01 ,因爲01是低位,所以存儲在最左邊。同理,最高位00,則存儲在最右邊。
  • 最終在內存看到的效果是,存儲的數據是倒着來的,也應該倒着讀數據。

模擬數組a的內存結構如下:

ptr2 對應的是指針pa
在這裏插入圖片描述

表達式 (int * ) ( (int)a + 1)的功能爲:

  • 先將數組名a,強制轉換爲int類型。因爲a代表的是數組的首元素地址,例如0x001,轉換爲int類型即爲1
  • (int)a + 1,得到一個整數,即爲2
  • 最後這個整數地址,再強制轉換爲指針變量(再轉爲16進制),即爲ox0002,其實就是之前首元素的第二個字節位置
2000000

爲了加深理解,如下代碼看着估計更清晰明瞭:

int main(void)

{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)((int)a + 1);
	int* ptr2 = (int*)((int)a + 2);
	int* ptr3 = (int*)((int)a + 3);
	int* ptr4 = (int*)((int)a + 4);
	int* ptr5 = (int*)((int)a + 5);
	printf("%x \n" ,*ptr1);
	printf("%x \n" ,*ptr2);
	printf("%x \n" ,*ptr3);
	printf("%x \n" ,*ptr4);
	printf("%x \n" ,*ptr5);

	return 0;
}


2000000			// 2 00 00 00	
20000			// 2 00 00
200				// 2 00
2				// 2
3000000			// 3 00 00 00

如上代碼如果還未明白,可移步如下地址:
https://blog.csdn.net/RationalGo/article/details/17341083?utm_source=blogxgwz0





4.&數組名與整數的加減(數組地址的偏移)

#include <stdio.h>

int main() {

	int* pa;
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	pa = &a;
	printf("%x   \n", &a);
	printf("%x   \n", &a[1]);
	printf("%x   \n", &a[2]);
	printf("%x   \n", &a + 1);		// 注意:此處&a代表的是整個數組的地址,長度是40個字節
	return 0;
};



3dfe08			// a[0]地址
3dfe0c			// a[1]地址
3dfe10			// a[2]地址
3dfe30			// &a + 1地址

通過結果表明:

  • 相鄰元素之間的地址確實相差4個字節,32位系統中,int類型確實佔用4個字節大小
  • 3dfe30與3dfe08兩個地址相差是40,也就是說,&a+1向右邊偏移了40個字節,就是整個數組的長度。(如果數組a的長度爲5,那麼將移動20個字節)




根據如上的原理,那麼如下的代碼中,*(p_last - 1),最終能打印什麼呢?


int main() {

	int* pa;
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	pa = &a;
	printf("%x   \n", &a);
	printf("%x   \n", &a[1]);
	printf("%x   \n", &a[2]);
	printf("%x   \n", &a + 1);
	int* p_last = &a + 1;
	printf("%d  \n", *(p_last - 1));
		
	return 0;
};


在這裏插入圖片描述

50f6b4
50f6b8
50f6bc
50f6dc
10

根據如上2個案例的說明:

  • int* p_last = &a + 1,說明指針p_last指向的位置,在緊鄰着數組a(佔用40個字節)的右邊,如上圖所示位置
  • *(p_last - 1)指針p_last向左偏移4個字節,然後取值,最終查看到的內容爲10
  • 上面 (p_last - 1) 如果更換爲p_last -1 最終會打印亂碼,所以案例一中的原理我猜想也是*(p-1)是最合適的寫法




5.變量名與整數的加減

#include <stdio.h>

int main() {

	int a = 20;
	double b = 30.22;
	printf("%p  \n", &a);
	printf("%p  \n", &a+0x1);		// 0x1 其實就是0x00000001
	printf("%p  \n", &b);
	printf("%p  \n", &b+0x1);
	return 0;
};

0075F724
0075F728
0075F714
0075F71C

  • 根據如上代碼發現:
  • 變量名加一(0x1),最終移動的字節數和變量名的類型有關,int佔4個字節、double佔8個字節,所以分別移動4個和8個字節。





以上所有案例總結:

  • 假設數組名a,a+1移動幾個字節數,由數組的類型決定的,1 * sizeof(數組類型)
  • &a+1,移動的大小等於整個數組所佔字節大小
  • int(a) + 1,這個代表將首數組首元素的地址加上1,最終計算的地址,一般還需要再換位換指針變量
    假設變量名b,則b+0x1移動的字節數,也是有這個變量的類型決定的
    假設指針變量p,則p+ 1,代表p指向的內容移動一個大小。如果p指向一維數組,則p移動一個元素。如果p指向結構體,則p + 1 移動的位置大小是這個結構體所佔的字節大小

在這裏插入圖片描述

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