C語言第09天學習筆記:數組(二維數組與字符數組)
內容提要
- 數組
- 二維數組
- 字符數組
二維數組
定義
二維數組本質上是一個行列式組合,由行和列兩部分組成,屬于多維數組,通過行和列解讀(先行后列)。
二維數組可被視為一個特殊的一維數組,即當一個數組中的每一個元素是一維數組的時候,這個數組就是二維數組。
語法
數據類型 數組名[行容量][列容量];
- 行容量:外層數組的數組容量
- 列容量:內層數組的數組容量
說明
- 二維數組在初始化的時候,可以省略行數,系統會通過初始化后的數據自動推斷行數。
- 二維數組和一維數組一樣,也可以部分初始化,未初始化的元素使用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]);```代碼:
```c
#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;
}
運行結果:
11 0 0
21 22 0
31 32 33
案例2:矩陣的轉置
分析:所謂的轉置,就是原本的列變行,行變列。
例如,2行3列矩陣轉置為3行2列矩陣:
11 | 12 | 13 |
---|---|---|
21 | 22 | 23 |
轉置后:
11 | 21 |
---|---|
12 | 22 |
13 | 23 |
代碼:
#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;
}
字符數組
概念
元素類型為char
(字符型)的數組叫做字符數組,往往用來存儲字符串數據。C語言中的字符是字節字符(1字符=1字節,1char=8bit)。
硬件中存放數據以bit(位)為單位,系統對于內存的操作以char(字節)為單位,系統為內存以1個字節為單位進行編號。
實驗:
char a = 'A'; // 正確
char b = '1'; // 正確,ASCII碼:49
char c = 1; // 正確,ASCII碼:1
char d = '\n'; // 正確,只要其對應的ASCII碼的范圍在0~127之間,都屬于字符
char e = "A"; // 錯誤,雙引號括起來的內容叫做字符串常量
char f = '馮'; // 錯誤,中文字符不在0~127這個范圍內
語法
- 一維數組:
char 數組名[數組容量];
- 二維數組:
char 數組名[行容量][列容量];
字符數組的語法與之前所學的一維數組和二維數組的語法類似,只不過數據類型是char
。
注意:如果char數組初始化的時候,沒有完全初始化值,未初始化部分使用\0
(\0
對應的ASCII值是0)進行填充,\0
無法通過printf
等打印輸出到控制臺。
例如:
char c[8] = {'h','e','l','l','o'}; // 等價于 char c[8] = {'h','e','l','l','o','\0','\0','\0'};
案例
案例1:輸出一個字符序列(I LOVE YOU)
代碼:
#include <stdio.h>int main(int argc,char *argv[])
{// 創建一個數組,用來存儲I LOVE YOU,空格' '也是字符,對應的ASCII為32char arr[] = {'I',' ','L','O','V','E',32,'Y','O','U'};// 計算數組的大小int len = sizeof(arr) / sizeof(arr[0]);// 遍歷數組for (int i = 0; i < len; i ++)printf("%c",arr[i]);printf("\n");return 0;
}
案例2:輸出一個用字符*
組成的空菱形圖案
代碼:
#include <stdio.h>int main(int argc,char *argv[])
{// 創建一個二維數組,存放菱形char arr[5][5] = {{' ', ' ', '*', ' ', ' '},{' ', '*', ' ', '*', ' '},{'*', ' ', ' ', ' ', '*'},{' ', '*', ' ', '*', ' '},{' ', ' ', '*', ' ', ' '}};// 計算行和列的大小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("%c",arr[i][j]);printf("\n");}printf("\n");return 0;
}
注意
- 如果定義時,不初始化,元素值不確定(局部作用域)
char arr1[2]; // 此時,這個數組中元素的值是隨機值 char arr2[3] = {'a','b','c'}; // 完全初始化 char arr3[3] = {}; // 此時所有的元素使用 \0 填充 char arr4[3] = {0}; // 此時所有的元素使用 \0 填充
- 如果提供的字符個數大于數組長度,則按照語法錯誤處理(會報警告,但是能編譯通過);如果字符個數小于數組長度,后面的元素自動補
\0
char arr1[2] = {'h','e','e'}; // 編譯通過,但是會報警告(warning),不建議寫,實際存放的是he char arr2[3] = {'a'}; // 正確寫法,部分初始化,未初始化部分補 \0
- 如果提供的字符個數與數組長度相同,可以省略數組長度,系統會自動確定元素的個數,適合字符較多時
char arr1[] = {'b','u'}; // 正確,根據初始化元素,由系統自動計算元素個數
字符串結束標志
- C語言規定,字符串常量以字符
\0
作為結束標志。 - 編譯系統對字符串常量自動加一個
\0
作為結束標志,例如char *name = "tom"
,實際存儲為{'t','o','m','\0'}
。 - 程序中往往通過判斷
\0
來檢測字符串是否結束,例如while(arr[i] != '\0') {..}
。 \0
的ASCII碼是0,不是一個可顯示可輸出的字符,是“空操作符”,僅僅用作一個工程判別的標志或者在數組中占位。
例如:
char a[] = {'h','i'}; // 輸出:hi
char a[] = {'h','i','\0'}; // 輸出:hi
char c[] = "hello"; // 輸出:hello,實際存儲:hello\0,將字符串常量賦值給字符數組
字符數組的多樣表示
char數組可以以數組的形式一個一個輸出每個字符,也可以以字符串的形式整體輸出。
演示:
#include <stdio.h>int main(int argc,char *argv[])
{// 字符串的第1種表示:char s1[] = {'h','e','l','l','o','w','o','r','l','d','\0'};// 字符個數:12// 字符串的第2種表示:char s2[] = {"hello world"};// ""包裹的內容是字符串常量,字符串常量默認末尾有一個\0,字符個數:12// 字符串的第3種表示:char s3[] = "hello world"; // 字符個數:12// 字符串的第1種輸出:// 計算字符串所占字節數printf("s1=%lu,s2=%lu,s3=%lu\n", sizeof(s1), sizeof(s2), sizeof(s3)); // s1=12,s2=12,s3=12// 計算數組大小int len = sizeof(s3) / sizeof(s3[0]);// 遍歷for (int i = 0; i < len; i++){// 過濾\0if (s1[i] == 0 || s2[i] == '\0' || s3[i] == 0)continue;printf("%c,%c,%c\n", s1[i], s2[i], s3[i]);}printf("\n");// 字符串的第2種輸出:printf("%s,%s,%s\n",s1,s2,s3);printf("\n");return 0;
}
注意
- 字符串的長度與字符數組的長度不一定相同。
char name[] = "hello"; // 數組長度:6,字符串長度:5
- 利用字符串常量可以對字符數組進行初始化,但不能用字符串常量對字符數組賦值。
// 正確演示:利用字符串常量給字符數組初始化 char arr1[] = "hello";// 錯誤演示:利用字符串常量給字符數組賦值 char arr2[6]; arr2 = "hello"; // 錯誤,數組名是常量,不能被賦值
一維數組練習題
- 鍵盤錄入一組數列,利用冒泡排序將數據由大到小排序
- 從鍵盤輸入年、月、日,計算并輸出該日是該年第幾天
- 鍵盤錄入一組數列,求最大數、最小數、均值
- 從鍵盤錄入一組數列,判斷是否是回文,舉例:12321,abba,121
- 用數組存儲10個整型數,通過鍵盤輸入一個數,找出該數在數組中的下標值
- 通過鍵盤輸入 10 個學員成績:
1)輸出不及格學員的成績和下標。
2)求最高分的下標值
3)求最低成績的下標值
4)求總成績及平均成績
二維數組練習題
- 一個二維數組賦了初值,用戶輸入一個數,在該二維數組中查找。找到則返回行列位置,沒找到則提示。
- 二維整型數組,求所有元素平均值,求每行最大值,求每列最小值。
- 在行列相等數組計算主對角線元素的和
- 計算一個矩陣下三角元素的和
- 電影院為了答謝影迷的支持,在某一排的某一列座位上放置了一個大禮包,放置禮物的位置具有這樣的規則(行和列的平方和為開店日期 512(5月12日)); 請設計程序找出大禮包的位置,(假定電影院有20排,每排25個座位)
字符數組練習題
- 編寫一個程序,讀取用戶輸入的字符串,并將其反轉輸出。
- 編寫一個程序,判斷用戶輸入的字符串是否為回文(即正反讀都一樣的字符串)。
思考題【選做】
- 求出一個矩陣的鞍點。鞍點的含義為行上最大同時列上也最大。
注::后續筆記將圍繞C語言知識展開,建議每日實操時長不少于3小時