【C語言從青銅到王者】第三篇·詳解陣列

2021-04-27 10:01:28

本篇前言

陣列就是一組相同型別元素的集合,通過陣列我們可以更為方便快捷的儲存和存取資料。理論上來說C語言可以構造n維陣列,但是由於一維陣列和二維陣列使用更加頻繁,所以本篇著重講解一維陣列與二維陣列,多維陣列僅作了解。



一維陣列

儲存一個數或字元我們只需要定義一個變數。那儲存100個數或字元呢?方便快捷的儲存和存取大量的資料,這就是陣列存在的意義。


建立一維陣列

語法:

陣列元素型別 陣列名 [整型常數表示式]

陣列元素型別指的是陣列中所有元素的資料型別,陣列名是陣列的名稱,方括號中的常數表示式的值代表陣列的元素個數(VS2019編譯環境下必須是常數表示式。DevC++編譯器支援C99語法,C99中有變長陣列的概念,允許使用變數表示式,所以小夥伴在devC++中使用變數定義陣列大小不會報錯。為了增加程式的容錯率建議仍使用常數表示式)。


一維整型陣列初始化

建立變數時就對變數進行賦值的操作叫做初始化。單純的建立陣列就好比我們造出了一堆空盒子,接下來要做的是往盒子裡放東西。
完全初始化:
完全初始化的意思是所有陣列元素都被初始化,即我們一上來就把盒子都放滿了

int a[5] = { 1,2,3,4,5 };

在這裡插入圖片描述
省略定義陣列元素個數則會自動完全初始化:

int a[] = { 1,2,3,4,5 };

在這裡插入圖片描述
不完全初始化:
指的是我們並沒有將所有元素都初始化

int a[5] = { 1,2,3 };

在這裡插入圖片描述

總結:一維整型陣列初始化的元素會從左到右被依次賦值,賦值數不能超過元素個數,未賦值元素預設為0,未標明元素個數的陣列則會自動完全初始化。


一維字元型陣列初始化

完全初始化:

char ch[5] = { 'h','e','l','l','o' };

在這裡插入圖片描述

char ch[5] = { 'h','e','l','l','\0' };

在這裡插入圖片描述

char ch[5] = { "hell" };

在這裡插入圖片描述
總結:字元型陣列可以一個元素一個元素的初始化也可以直接用字串初始化;區別是用字串初始化的時候會在末尾自動補"\0"作為字串結束的標誌。

再看這三種寫法的字串長度(strlen)和字元陣列的大小(sizeof):

#include<stdio.h>
#include<string.h>
int main()
{
	char ch1[5] = { 'h','e','l','l','o' };
	char ch2[5] = { 'h','e','l','l','\0' };
	char ch3[5] = { "hell" };
	printf("%d ", strlen(ch1));
	printf("%d ", strlen(ch2));
	printf("%d\n", strlen(ch3));
	printf("%d ", sizeof(ch1));
	printf("%d ", sizeof(ch2));
	printf("%d\n", sizeof(ch3));
	return 0;
}

在這裡插入圖片描述
總結:列印字串和strlen函數計算字串長度時,預設"\0"是其結束的標誌。ch1沒有"\0"作為字串的結束標誌,ch2有,ch3由於是字串完全初始化自動補了"\0"所以也有,所以strlen函數下,ch1的字串長度就是隨機值,ch2和ch3的長度是4。由於這三個陣列都初始化了5個char型別的變數,一個char是一位元組,所以它們的陣列大小都是5。

如果省略陣列元素個數則會自動完全初始化,下圖的程式和上圖的程式功能完全一致:
在這裡插入圖片描述


不完全初始化:

char ch1[5] = { 'h','e','l','l'};

在這裡插入圖片描述

char ch2[5] = { 'h','e','l','\0' };

在這裡插入圖片描述

char ch3[5] = { "hel" };

在這裡插入圖片描述
總結:不完全初始化的字元型陣列會自動補"\0"

再看它們的字串長度(strlen)和字元陣列的大小(sizeof):

#include<stdio.h>
#include<string.h>
int main()
{
	char ch1[5] = { 'h','e','l','l'};
	char ch2[5] = { 'h','e','l','\0' };
	char ch3[5] = { "hel" };
	printf("%d ", strlen(ch1));
	printf("%d ", strlen(ch2));
	printf("%d\n", strlen(ch3));
	printf("%d ", sizeof(ch1));
	printf("%d ", sizeof(ch2));
	printf("%d\n", sizeof(ch3));
	return 0;
}

在這裡插入圖片描述
由於不完全初始化補了"\0",所以ch1的字串長度是4,ch2和ch3是3。陣列長度不變,仍然都是5。

整體總結:
1.不完全初始化會自動補齊元素,整型補0,字元型補\0
2.使用單個字元初始化不會補\0,使用字串初始化時會自動補\0
3.自動補\0或者0的操作僅在陣列空間未滿時進行,若空間已滿則不會進行
4.\0是字串結束的標誌,strlen讀取的是\0之前的所有字元的個數


一維陣列的使用與記憶體中的儲存

存取具體某個陣列元素是用下標實現的,陣列下標的值從0開始,比如a[0]代表的是陣列a的第一個元素
例子:

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

在這裡插入圖片描述
一維陣列元素在記憶體中是連續存放的,隨著陣列下標的增長,地址由低到高變化:
例子:

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%p\n", &a[i]);
	}
	return 0;
}

在這裡插入圖片描述
這些地址是什麼意思呢?地址下標是16進位制,用0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F一共16個字元表示0~15的數位。經過計算可知,每個陣列元素的地址都差4,而且依次遞增。這個4是因為int型陣列每個陣列元素的大小是4位元組,如果是char型陣列,每個陣列的地址就差1位元組:

#include<stdio.h>
int main()
{
	char a[5] = { 1,2,3,4,5 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%p\n", &a[i]);
	}
	return 0;
}

在這裡插入圖片描述
陣列元素在記憶體中是連續存放的,這個特性使得我們可以通過陣列首元素的地址來存取整個陣列,也讓陣列的使用變得靈活起來


二維陣列

二維陣列,顧名思義是有兩個維度的陣列。
在這裡插入圖片描述

建立二維陣列

語法:

陣列元素型別 陣列名 [行數][列數];

int a[3][4];
char ch[3][4];

二維陣列初始化

僅介紹二維整型陣列的初始化:

1.順序初始化

int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

在這裡插入圖片描述

int a[3][4] = {1,2,3,4,5,6,7,8,9};

在這裡插入圖片描述

陣列會從第一行開始一行一行的順序填入元素,缺位補0(字元補\0)。

2.逐行初始化

int a[3][4] = { {1,2},{3,4,5},{6,7,8,9} };

在這裡插入圖片描述
每個子大括號表示一行的元素,缺位補0(字元補\0)

3.省略行數的初始化
C語言規定:二維陣列初始化可以省略行數,不能省略列數

int a[][4] = { {1,2},{3,4,5},{6,7,8,9} };

在這裡插入圖片描述


二維陣列的使用與記憶體中的儲存

二維陣列同樣用下標存取。下標同樣從0開始,前一個下標是行下標,後一個下標是列下標。
在這裡插入圖片描述

#include<stdio.h>
int main()
{
	int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
			printf("%-2d ", a[i][j]);
		printf("\n");
	}
	return 0;
}

在這裡插入圖片描述
二維陣列在記憶體中的儲存也是連續存放
在這裡插入圖片描述

#include<stdio.h>
int main()
{
	int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
			printf("%p\n", &a[i][j]);
	}
	return 0;
}

在這裡插入圖片描述


多維陣列

我們可以將陣列的下標看成座標,那麼一維陣列就是一個離散的數軸,二維陣列就是一個離散的平面直角座標系。那三維陣列其實就是一個離散的空間直角座標系,n維陣列就是一個離散的n維空間
在這裡插入圖片描述

n維陣列其實就是n個一維陣列相巢狀:n維陣列是由若干n-1維陣列組成的,每個n-1維陣列是由若干n-2維陣列組成的…一直到每個2維陣列是由若干一維陣列組成的。
大家理解概念就好,用的不多不細說。


陣列名到底是什麼?


除了定義陣列的時候使用陣列名,其他時候程式中出現陣列名的意義到底是什麼?

陣列名錶示整個陣列

只有兩個特殊情況下陣列名錶示整個陣列:
1.sizeof計算整個陣列大小時:

#include<stdio.h>
int main()
{
	int arr[10];
	printf("%d", sizeof(arr));
	return 0;
}

在這裡插入圖片描述
此時arr表示整個陣列,sizeof計算的是整個陣列的大小
2.取地址符+陣列名時:

#include<stdio.h>
int main()
{
	int arr[10];
	printf("%p", &arr);
	return 0;
}

在這裡插入圖片描述
此時取出的也是整個陣列的地址。這個地址和陣列首元素的地址的值是相同的,但是它代表的是陣列的地址而不是陣列首元素的地址,值相同,含義不同。


陣列名錶示陣列首元素地址

除了上面所說的兩種情況以外,其他所有情況下的陣列名均表示陣列首元素的地址。(我在【C語言從青銅到王者】第二篇·詳解函數中的傳址呼叫中也說了這一點)
給出一些例子佐證:
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述