數組
數組的引入
-
如果要在程序中保存一個人的年齡?如何保存?
答:創建一個基于int類型的變量,舉例:
int age = 22
-
如果要在程序中保存一個人的三門課的成績?如何保存?
答:創建三個基于float類型的變量,舉例:
float score1 score2 score3;
- 保存一個人15門課程的成績?如何保存? -----數組
數組的概念
什么是數組
定義:數組是相同類型,有序數據的集合。
數組的特征
- 數組中的數據被稱為數組的元素(所謂的元素,其實就是數組的每一個匿名的變量空間),是同構。
- 數組中的元素存放在內存空間(char player_name[6]: 申請在內存中開辟6塊連續的基于char類型的變量)
衍生概念:下標(索引)
- 下標(索引)代表了數組中元素距離第一個元素(首地址所在的元素)的偏移量。 舉例:第一個元素距離第一個元素的偏移量是0,所以數組中的下標是從0開始的。
- 數組的下標是從0開始的:
數組的最大下標 = 數組的元素個數(數組的大小或容量) - 1
int a:在內存中開辟1塊空間,該空間的大小是4個字節。
int a1,a2,a3,a4,a5:在內存中開辟連續的5塊空間,每一塊空間的大小是4個字節。但是不如數組方便。
int arr[5]:在內存中開辟連續的5塊空間,每一塊空間的大小是4個字節。
一維數組
數組的定義
語法:
數據類型 數組名[數組容量];
注意:數據類型又被稱作類型說明符,數組容量又被稱作數組元素個數或者數組的大小/長度。
說明:
-
數組的數據類型由數組中的元素來決定。也就是說數據類型,由元素的類型來決定,元素是什么類型,數組就是什么類型。同一個數組中,所有元素的類型都是一致的。
-
數組名也是標識符,我們所說的數組(名),大家可以理解為數據類型是數組的變量(名)。命名規則與變量的規則一樣,唯一的區別是變量使用單數,數組使用復數。也就是以字母或者下劃線開頭,后面只能跟字母、數字、下劃線。
-
數組容量還可以叫做常量表達式。**其值必須是整數。**關于數組容量的類型:
-
C89標準:只支持常量和符號常量,不支持變量。
#define SIZE 5 // 符號常量,使用宏定義int lenght = 5; // 變量int arr1[5]; // 常量(字面量)正確 int arr2[SIZE]; // 符號常量,正確 int arr3[length];// 變量,C89錯誤
-
C99標準(大部分環境支持):引入==變長數組(VLA)==的概念,就是可以使用變量,數組在運行的時候決定大小。舉例:
int length = 5; // length = 5; 創建一個變量length,賦值5 int arr[length]; // 創建一個容量為5的數組,C99標準下是正確的(執行這句的代碼的時候,已經在內存申請空間) length = 10; // 此時雖然變量length的值變成了10,但是已經創建的數組的大小是不會改變的。數組大小依然是5
-
類型:
? 代表了數組中元素的類型,數組的空間大小 = 數組中所有元素空間之和。
容量:
? 數組中能存儲多少個元素,數組容量一定是一個整型。
深入理解:
? ① 定義一個數組,相當于申請了一個可以容納所指定元素個數的內存單元。所申請的內存單元是連續的。
? ② 定義一個數組,相當于定義了多個匿名的變量,這些變量可以通過數組名[下標]
來訪問。
范例:
// 定義一個數組
int arr[10]; // 定義一個存放10個int類型元素的數組
關于數組中元素默認值問題:
**全局作用域和static修飾的變量:**元素的默認值是0
局部作用域:元素的默認值是隨機值,此時強烈建議用戶初始化。
舉例:
#include <stdio.h>int g_age; // 全局作用域,定義在函數的外面,默認值是0 int g_ages[5];// 全局作用域,等同于全局變量,元素的默認值是0int main(int argc, char *argv[]) {int p_age; // 局部作用域:函數中定義的所有的變量都屬于局部作用域。變量使用前需要初始化。int p_ages[5]; // 局部作用域:元素的值使用前需要初始化return 0; }
**注意:關于默認值 ,整型和浮點型的默認值是
0
,字符型的默認值是\0
,\0
**對應的ASCII碼為0
數組元素的訪問
原則:
? 數組中的元素不能一次性訪問所有,只能一個一個的訪問。
語法:
-
取值:
數組名[下標];
-
賦值:
數組名[下標] = 值;
舉例:
// 定義一個存儲10個元素的int數組
int arr[10];// 給數組的第一個元素進行賦值
arr[0] = 88;// 訪問數組的第一個元素
int a = arr[0];// 修改數組中第一個元素的值
arr[0] = 66; // 使用66覆蓋88int c = arr[9]; // 如果是一個局部作用域的數組,此時訪問的元素的值是 隨機值;如果是全局作用域,值是0
int b = arr[10];// error,報一個錯誤:下標越界異常,因為訪問了一個未知的存儲空間
案例
-
需求:利用循環給數組元素a[0]~a[9]賦值
0~9
,并且要求逆序輸出。 -
代碼:
#include <stdio.h>int main(int argc,char *argv[]) { // 創建一個數組,用來存放0~9int arr[10];// 計算數組的大小:數組大小 = 數組所有元素的總字節數 / 每一個元素的字節數 需要使用到sizeof運算符int len = sizeof(arr) / sizeof(arr[0]);// 通過for循環給數組賦予0~9for (int i = 0; i <= 9; i++) arr[i] = i;// 逆序輸出數組中的元素:數組最大下標 = 數組大小 - 1// 使用for循環獲取數組每一個元素稱之為數組的遍歷for (int j = len -1; j >= 0; j--) printf("%4d", arr[j]);printf("\n");return 0; }
結果:
數組的初始化
說明:所謂的初始化,就是定義數組的時候,用指定的數據給對應的元素賦值。
語法:
數據類型 數據組[數組容量] = {...};
注意事項:
- 數組可以部分初始化:也就是可以給數組中的前幾個元素初始化,未被初始化的元素系統將自動初始化,初始值是0。(也就是一個數組織中,一旦有元素被初始化,剩余的元素將自動初始化為0)
// 數組的部分初始化
int arr[10] = {11,12,13,14,15}; // 推薦寫法:只初始化前5個元素,剩余元素系統默認為0,等價于下面寫法
int arr[10] = {11,12,13,14,15,0,0,0,0,0};char arr1[5] = {'a','b','c'}; // 推薦寫法,等價于下面寫法
char arr1[5] = {'a','b','c','\0','\0'}; // 等價于下面寫法
char arr1[5] = {'a','b','c',0,0}; // '\0' 對應的ASCII碼是 0 char a = 'A' 等價于 char a = 65int arr2[5] = {}; // 等價于下面第三種寫法
int arr2[5] = {0};// 等價于下面第三種寫法,推薦
int arr2[5] = {0,0,0,0,0};
-
數組根據初始化的元素自動分配大小:如果定義數組時未指定數組容量,則系統會根據初始化的元素的個數來決定容量。
// 由初始化的元素來決定數組的容量 int arr[] = {11,12,13,14,15}; // 推薦寫法,等價于下面寫法 int arr[5] ={11,12,13,14,15};
案例
案例1
-
需求:求斐波拉契數列,限制在20個。
-
分析:1,1,2,3,5,8…
-
代碼:
#include <stdio.h>int main(int argc,char *argv[]) {//定義循環變量int i;//定義一個數組,用來存儲20個數列int f[20] = {1,1};//計算數組的大小int len = sizeof(f) / sizeof(f[0]);//通過一個for循環完成數列for( i = 2; i < len; i++) f[i] = f[i-1] + f[i-2];//{1,2,3,4}//遍歷數組for(i = 0; i < len;i++){//一行顯示5個數if (i > 0 && i % 5 == 0) printf("\n");printf("%8d",f[i]);}printf("\n");return 0; }
結果:
案例2
-
需求:從鍵盤輸入年、月、日,計算并輸出該日是該年第幾天。
-
分析:
- 首先創建一個數組,用來存放每一個月的天數,因為二月特殊,初始化的時候,默認為平年天數。
- 從控制臺輸入年,月,日
- 閏年校驗:如果是閏年,就修改數組中二月份對應的天數(平年:28天,閏年:29天)。
- 定義一個變量,用來記錄天數,默認就是我們輸入的天數。
- 遍歷數組,將輸入月份之前的每一個月的天數取出來加到記錄天數的變量中。
- 將統計后的天數打印輸出。
-
代碼:
#include <stdio.h>int main(int argc,char *argv[]) {// 首先創建一個數組,用來存放每一個月的天數,因為二月特殊,初始化的時候,默認為平年天數。int t[] = {31,28,31,30,31,30,31,31,30,31,30,31};// 從控制臺輸入年,月,日int year,month,day;printf("請輸入年份、月份、天(yyyy-MM-dd):");scanf("%d-%d-%d", &year, &month, &day);// 閏年校驗:如果是閏年,就修改數組中二月份對應的天數(平年:28天,閏年:29天)。if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) t[1] = 29;// 定義一個變量,用來記錄天數,默認就是我們輸入的天數。int sum = day;// 遍歷數組,將輸入月份之前的每一個月的天數取出來加到記錄天數的變量中。for (int i = 0; i < month - 1; i++) sum += t[i];// 將統計后的天數打印輸出。printf("%d月%d日是%d年的第%d天!\n", month, day, year, sum);return 0; }
結果:
二維數組
定義
二維數組本質上是一個行列式組合,也就是說二維數組是由行和列兩部分組成,屬于多維數組。二維數組通過行和列解讀(先行后列)
二維數組可被視為一個特殊的一維數組,也就是說,當一個數組中的每一個元素是一位數組的時候,那么這個數組就是二維數組。
語法
數據類型 數組名[行容量][列容量];
行容量:外層數組的數組容量
列容量:內存數組的數組容量
說明
- 二維數組在初始化的時候,可以省略行數,系統會通過初始化后的數據自動推斷行數。
- 二維數組和一位數組一樣,也可以部分初始化,未初始化的元素使用
0
。 - 二維數組在初始化的時候,不能省略列數,否則編譯報錯。
舉例
int arr[3][3] = {{11,12,13},{21,22,23},{31,32,33}}; // 正確,等價于下面寫法
int arr[][3] = {{11,12,13},{21,22,23},{31,32,33}}; // 正確,二維數組初始化的時候可以省略行容量,推薦int arr[3][3] = {{11,12},{21,22,23},{31}}; // 正確,可以未初始化部分補0,等價于下面寫法
int arr[3][3] = {{11,12,0},{21,22,23},{31,0,0}}; // 正確,支持部分初始化int arr[3][3] = {0}; // 正確,所有位置使用0補齊,推薦
int arr[3][3] = {}; // 正確,所有位置使用0補齊
int arr[3][3] = {11}; // 正確,除了0行0列是11外,其他都用0補齊int arr[][] = {{11,12,13},{21,22,23},{31,32,33}}; // 錯誤,編譯報錯,不能省略列容量
int arr[3][] = {{11,12,13},{21,22,23},{31,32,33}}; // 錯誤,編譯報錯,不能省略列容量int arr[][3] = {11,12,13,21,22,23,31,32,33}; // 正確,{}中不一定要嵌套
int arr[][3] = {11,12,13,21}; // 正確,{}中不一定要嵌套
注意:在C語言中,二維數組在計算機的存儲順序是按行進行的,即第一維(行)下標變化慢,第二維的(列)下標變化快。
內存存儲
注意:地址這里只是為了區分,實際的地址表示為十六進制。
應用場合
主要是應用對行列有要求的情況。比如說我們現在要存儲西安粵嵌所有在班學生的成績。
還有就是字符數組的應用,比如用數組存儲學生的姓名。
double scores[35] = {..};
一維數組初始化,存放1個班所有學生的成績
double scores[5][40] = {{..}..}
二維數組初始化,存放5個班的學生成績,每個班最多40人。
double scores[6][10][40] = {{{..}..}..}
三維數組初始化,存放6個校區、每校區最多10各班,每班最多40人。
特殊寫法
-
下標可以是整型表達式。如:
a[2-1][2*2-1]
→a[1][3]
-
下標可以是已經有值的變量或者數組元素。如:
a[2*x-1][b[3][1]]
[]中最終需要的是一個>0的整數。 -
數組元素可以出現在表達式中。如:
b[1][2] = a[2][3]/2
-
演示:
數組:arr 列-0 列-1 列-2 舉例 說明 行-0 11 12 13 arr[0][1]
數組arr的0行1列對應的元素 行-1 21 22 23 arr[1][2]
數組arr的1行2列對應的元素
注意:使用數組元素的下標應在已定義數組的大小范圍內;應注意區別定義數組大小和引用數組元素的區別。
初始化
-
分行給二維數組賦初值
int arr[3][4] = {{11,12,13,14},{21,22,23,24},{31,32,33,34}};
-
可將所有數據寫在一個{}內,按照排列順序對運算賦值
int arr[3][4] = {11,12,13,14,21,22,23,24,31,32,33,34};
-
可對部分元素賦初值,其余未初始化部分自動填充
0
int arr[3][4] = {{11},{21,22},{31,32,33}};
-
若對全部元素賦初值,自定義數組時可以省略第一維數組容量(行容量),第二維數組容量(列容量)必須指明。
int arr[][4] = {{11,12,13,14},{21,22,23,24},{31,32,33,34}}; int arr[][4] = {11,12,13,14,21,22,23,24,31,32,33,34};
-
在分行賦初值時,也可以省略第1維的長度(行容量)。
int arr[][4] = {{11,12,13},{0},{0,10}};
案例
案例1
-
需求:二維數組的遍歷
-
分析:
-
二維數組本質上屬于行列式,遍歷的時候需要借助于嵌套的for循環,外層for負責行的遍歷,內層for負責列的遍歷。
-
取值:
數組名[行容量][列容量];
-
賦值:
數組名[行下標][列下標] = 值;
-
行和列的大小計算:
// 計算行的大小 int row_length = sizeof(數組名) / sizeof(數組名[行下標0]); // 計算列的大小(每一行的列數是相同) int col_length = sizeof(數組名[行下標0]) / sizeof(數組名[行下標0][列下標0]);
-
-
代碼:
#include <stdio.h>int main(int argc,char *argv[]) {// 創建一個二維數組int arr[][3] = {{11},{21,22},{31,32,33}};// 獲取行和列的大小int row_len = sizeof(arr) / sizeof(arr[0]);// 外層數組大小int col_len = sizeof(arr[0]) / sizeof(arr[0][0]);// 內層數組大小,因為每個內層數組大小一致,所以計算第一個就可以了// 遍歷數組// 外層循環:遍歷行for (int i = 0; i < row_len; i++){// 內層循環:遍歷列for (int j = 0; j < col_len; j++){// 輸出元素printf("%-4d", arr[i][j]);}}printf("\n");return 0; }
-
運行結果:
案例2
-
需求:矩陣的轉置
-
分析:
-
所謂的轉置,就是原本的列變行,行變列。
-
-
代碼:
#include <stdio.h>#define ROW 2 #define COL 3int main(int argc,char *argv[]) {// 定義循環變量int i,j;// 準備2個數組,用來存放轉置前后的數列int arr_before[ROW][COL] = {11,12,13,21,22,23};int arr_after[COL][ROW] = {0};// 計算數組的大小int arr_before_row = sizeof(arr_before) / sizeof(arr_before[0]);int arr_before_col = sizeof(arr_before[0]) / sizeof(arr_before[0][0]);int arr_after_row = sizeof(arr_after) / sizeof(arr_after[0]);int arr_after_col = sizeof(arr_after[0]) / sizeof(arr_after[0][0]);// 循環遍歷二維數組printf("\n轉置前:\n");for (i = 0; i < arr_before_row; i++){for (j = 0; j < arr_before_col; j++){// 打印轉置前的數據printf("%-4d",arr_before[i][j]);// 轉置:行變列,列變行arr_after[j][i] = arr_before[i][j];}printf("\n");}printf("\n");printf("轉置后:\n");for (i = 0; i < arr_after_row; i++){for (j = 0; j < arr_after_col; j++){printf("%-4d",arr_after[i][j]);}printf("\n");}printf("\n");return 0; }
-
運行結果: