1. 程式人生 > >17. C語言 -- 指標和陣列的關係

17. C語言 -- 指標和陣列的關係

本部落格主要內容為 “小甲魚” 視訊課程《帶你學C帶你飛》【第一季】 學習筆記,文章的主題內容均來自該課程,在這裡僅作學習交流。在文章中可能出現一些錯誤或者不準確的地方,如發現請積極指出,十分感謝。
也歡迎大家一起討論交流,如果你覺得這篇文章對你有所幫助,記得評論、點贊哦 ~(。・∀・)ノ゙

1. 指標和陣列的關係

  指標和陣列之間的關係雖然十分微妙,但是不可以認為指標就是陣列,因為陣列名是陣列第一個元素的地址,也是陣列的首地址

  比如說下面的這段程式

#include <stdio.h>

int main()
{
	int a;
	int
*p = &a; printf("請輸入一個整數:"); scanf("%d", &a); printf("a = %d\n", a); printf("請重新輸入一個整數:"); scanf("%d", p); printf("a = %d\n", a); return 0; }

我們用整型變數 a 的地址初始化指標變數 p,其中 scanf("%d", &a); 的含義是將輸入存放在 &a 這個地址所指向的變數中,所以我們列印輸出 a 的值,就是通過 scanf 得到的輸入的值。之後我們需要重新輸入一個整數,因為 scanf 是將輸入的數字儲存到某一個地址中,如果不使用 &a ,而是使用 p 來表示地址也是可以的。執行上面這段程式碼,可以獲得如下的結果

請輸入一個整數:13
a = 13
請重新輸入一個整數:23
a = 23

這也強化了上節課所講的一個概念,就像整型變數存放整型數字一樣,指標變數用來存放地址;就像可以將整型變數當作是整型數字來使用一樣 (比如說一個整形變數 a =3, b= 4,那麼 a+b 實際上就可以看作是 3+4),指標變數也可以當作是地址來使用

  比如說現在我們想要輸入的是一個字串,可以使用下面的這段程式

#include <stdio.h>

int main()
{
	char str[128];

	printf("請輸入魚C的域名:");
	scanf("%s", str)
; printf("魚C工作室的域名是:%s\n", str); printf("str 的地址是:%p\n", str); printf("str 的地址是:%p\n", &str[0]); return 0; }

在這裡我們可以看到,在 scranf 中使用的是 str 而且他的前面並沒有 & 符號,這是為什麼呢?原因很簡單,因為陣列名是陣列第一個元素的地址,也是陣列的首地址。執行上面的程式碼可以得到如下的結果

請輸入魚C的域名:fishc.com
魚C工作室的域名是:fishc.com
str 的地址是:0x7ffd75ce3930
str 的地址是:0x7ffd75ce3930

在程式中,最後兩個列印輸出的佔位符都是 %p,也就是最後兩個輸出的都是指標,並且從實驗結果可以看出的確陣列名是陣列第一個元素的地址,也是陣列的首地址

2. 陣列中每個元素的地址

  由於陣列是一堆型別一致的變數挨個排放在一起的,所以第二個元素的地址就應該是第一個元素地址加上第一個元素所佔的空間。

  在下面的這個例子中,我們初始化了多種不同型別的陣列,並打印出陣列中每一個元素的地址

#include <stdio.h>

int main()
{
	char a[] = "FishC";
	int b[5] = {1, 2, 3};
	float c[5] = {1.1, 2.2, 3.3};
	double d[5] = {1.1, 2.2, 3.3};
	
	printf("a[0] -> %p, a[1] -> %p, a[2] -> %p\n", &a[0], &a[1], &a[2]);
	printf("b[0] -> %p, b[1] -> %p, b[2] -> %p\n", &b[0], &b[1], &b[2]);
	printf("c[0] -> %p, c[1] -> %p, c[2] -> %p\n", &c[0], &c[1], &c[2]);
	printf("d[0] -> %p, d[1] -> %p, d[2] -> %p\n", &d[0], &d[1], &d[2]);
	
	return 0;
}

執行上述程式碼可以得到如下的結果

a[0] -> 0x7fff94f04e80, a[1] -> 0x7fff94f04e81, a[2] -> 0x7fff94f04e82
b[0] -> 0x7fff94f04e10, b[1] -> 0x7fff94f04e14, b[2] -> 0x7fff94f04e18
c[0] -> 0x7fff94f04e30, c[1] -> 0x7fff94f04e34, c[2] -> 0x7fff94f04e38
d[0] -> 0x7fff94f04e50, d[1] -> 0x7fff94f04e58, d[2] -> 0x7fff94f04e60

對於字串陣列 a,相鄰的兩個元素之間的間隔是 1,int 型的間隔是 4,float 的間隔是 4,double 的間隔是 8,通過計算髮現確實與上面所講的是相同的。

3. 指標的運算

  如果用一個指標遍歷陣列,應該怎麼做呢?假如我們已經定義並初始化了一個數組,並且定義了一個指標。因為陣列的名字就是陣列第一個元素的地址,所以語句 1 和語句 2 是等價的,都是將陣列 a 的首地址存放到指標變數 p 中。

int a[] = {1, 2, 3, 4, 5};
int *p;
p = a; // 語句1
p = &a[0]; // 語句2

如果想用一個指標遍歷一個數組,只需將陣列的指標指向陣列的第一個元素的地址即可。陣列中接下來的每一個元素只需要對他進行指標的運算就可以了。

  當指標指向陣列元素的時候,我們可以對指標變數進行加減運算,這樣做的意義相當於指向距離指標所在位置向前或向後第 n 個元素。比如 p+1 表示 p 指標指向的元素的下一個元素;p-1 則表示指向上一個元素。需要鄭重強調的是:p+1 並不是簡單地將地址加 1,而是指向陣列的下一個元素

  比如說下面這段程式碼,我們通過指標的運算來獲取陣列中元素的值,其中需要注意的是指標的運算要寫成 *(p+1) 的形式,而不是 *p+1 的形式。

#include <stdio.h>

int main()
{
	int b[5] = {1, 2, 3, 4, 5};
	int *p = b;

	printf("*p = %d, *(p+1) = %d, *(p+2) = %d\n", *p, *(p+1), *(p+2));
	
	return 0;
}

執行上面這段程式碼可以獲得如下的結果

*b = 1, *(b+1) = 2, *(b+2) = 3

  通過結果可以看到,指標加1,並不是指向下一個地址,而是直接指向下一個元素。比如說上面的這個例子,程式是如何判斷他應該走多少個地址來找到下一個元素呢?實際上是通過資料型別,因為在定義陣列的時候,已經給出了陣列中元素的資料型別,比如整型就需要再走四個地址。

  實際上通過指標訪問陣列,並不一定要定義一個指標,因為陣列名本身就是陣列中第一個元素的指標,所以上面的程式還可以簡化為如下的形式

#include <stdio.h>

int main()
{
	int b[5] = {1, 2, 3, 4, 5};
	printf("*b = %d, *(b+1) = %d, *(b+2) = %d\n", *b, *(b+1), *(b+2));
	
	return 0;
}

  下面的這個例子就比較special了。使用指標定義一個字串,但是用陣列的方式訪問

#include <stdio.h>
#include <string.h>

int main()
{
	char *str = "I love FishC.com!";
	int i, length;

	length = strlen(str);

	for (i = 0; i < length; i++)
	{
		printf("%c", str[i]);
	}
	printf("\n");

	return 0;
}

之所以可以用指標來定義一個字串,是因為在 c 語言中一個字串就是一個字元陣列,所以它本質上還是一個數組,這個時候 str 代表陣列中第一個元素的地址,也就是陣列名,這也是他可以通過陣列形式訪問的原因。有的同學可能會問,為什麼是陣列第一個元素的地址,而不是整個陣列的地址,這個將會在下節課的內容中介紹。執行程式碼可以得到如下的結果

I love FishC.com!

很明顯程式碼是成功的。在上面的程式碼中將 printf("%c", str[i]); 改為 printf("%c", *str++); 實驗發現也是也是成功的。

  所以陣列和指標之間的關係就是,陣列名是陣列第一個元素的地址,也是陣列的首地址

參考

[1] “小甲魚” 視訊課程《帶你學C帶你飛》【第一季】P22

歡迎大家關注我的知乎號(左側)和經常投稿的微信公眾號(右側)

在這裡插入圖片描述