數組的基本概念
本小節知識點:
- 【理解】數組的基本概念
- 【理解】數組的分類
1.數組的基本概念
-
數組,從字面上看,就是一組數據的意思,沒錯,數組就是用來存儲一組數據的
-
在C語言中,數組屬於構造數據類型。一個數組可以分解爲多個數組元素,這些數組元素可以是基本數據類型或是構造類型。
- 注意:只能存放一種類型的數據
-
數組的幾個名詞
- 數組:一組具有相同數據類型的數據的有序的集合
- 數組元素:構成數組的數據。數組中的每一個數組元素具有相同的名稱,不同的下標,可以作 爲單個變量使用,所以也稱爲下標變量。
- 數組的下標:是數組元素的位置的一個索引或指示。(從0開始)
- 數組的維數:數組元素下標的個數。根據數組的維數可以將數組分爲一維、二維、三維、多維 數組。
-
數組的應用場景
- 一個int類型的變量能保存一個人的年齡,如果想保存整個班的年齡呢?
- 第一種方法是定義很多個int類型的變量來存儲
- 第二種方法是只需要定義一個int類型的數組來存儲
- 一個int類型的變量能保存一個人的年齡,如果想保存整個班的年齡呢?
2.數組的分類
-
按存儲的內容分類
- 數值數組:用來存儲數值得
- 字符數組:用來存儲字符 ‘a’
- 指針數組:用來存放指針(地址)的
- 結構數組:用來存放一個結構體類型的數據
- ... ...
-
按維度分類
- 一維數組
- 二維數組
- 多維數組
數組的定義、初始化、使用
本小節知識點:
- 【掌握】定義數組
- 【掌握】初始化數組
- 【掌握】數組的使用
1.定義數組
- 元素類型 數組名[元素個數];
- 示例:
int ages[10];
2.初始化數組
-
一般會在數組定義的同時進行初始化
- 其中在{ }中的各數據值即爲各元素的初值,各值之間用逗號間隔
int ages[3] = {4, 6, 9};
- 其中在{ }中的各數據值即爲各元素的初值,各值之間用逗號間隔
-
指定數組的元素個數,對數組進行部分顯式初始化
- 定義的同時對數組進行初始化,沒有顯式初始化的元素,那麼系統會自動將其初始化爲0
int nums[10] = {1,2};
- 定義的同時對數組進行初始化,沒有顯式初始化的元素,那麼系統會自動將其初始化爲0
-
不指定元素個數,定義的同時初始化,它是根據大括號中的元素的個數來確定數組的元素 個數
int nums[] = {1,2,3,5,6};
-
指定元素個數,同時給指定元素進行初始化
int nums[5] = {[4] = 3,[1] = 2};
-
先定義,後初始化
int nums[3]; nums[0] = 1; nums[1] = 2; nums[2] = 3;
-
沒有初始化會怎樣?
- 如果定義數組後,沒有初始化,數 組中是有值的,是隨機的垃圾數,所以如 果想要正確使用數組應該要進行初始化。
int nums[5]; printf("%d\n", nums[0]); printf("%d\n", nums[1]); printf("%d\n", nums[2]); printf("%d\n", nums[3]); printf("%d\n", nums[4]); 輸出結果: 0 0 1606416312 0 1606416414
- 注意:對於數組來說,一旦有元素被初始 化,其他元素都被賦值0
- 如果定義數組後,沒有初始化,數 組中是有值的,是隨機的垃圾數,所以如 果想要正確使用數組應該要進行初始化。
3.數組的使用
- 通過下標(索引)訪問:
ages[0]=10; int a = ages[2]; printf("a = %d", a);
數組注意事項
本小節知識點:
- 【掌握】數組注意事項
1.數組注意事項
-
在定義數組的時候[]裏面只能寫整型常量或者是返回整型常量的表達式
int ages4['A'] = {19, 22, 33}; printf("ages4[0] = %d\n", ages4[0]); int ages5[5 + 5] = {19, 22, 33}; printf("ages5[0] = %d\n", ages5[0]); int ages5['A' + 5] = {19, 22, 33}; printf("ages5[0] = %d\n", ages5[0]);
-
錯誤寫法
// 沒有指定元素個數,錯誤
int a[];
[]中不能放變量
int number = 10;
int ages7[number]; // 不報錯, 但是沒有初始化, 裏面是隨機值
printf("%d\n", ages7[4]);
int number = 10;
int ages7[number] = {19, 22, 33} // 直接報錯
int ages8[5];
// 只能在定義數組的時候進行一次性(全部賦值)的初始化
int ages10[5];
ages10 = {19, 22, 33};
// 一個長度爲n的數組,最大下標爲n-1, 下標範圍:0~n-1
int ages11[4] = {19, 22, 33}
ages[8]; // 數組角標越界
數組遍歷
本小節知識點:
- 【掌握】數組的遍歷
- 【掌握】數組長度計算方法
- 【掌握】練習
1.數組的遍歷
-
數組的遍歷:遍歷的意思就是有序地查看數組的每一個元素
-
示例 ``` int ages[4] = {19, 22, 33, 13}; for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
##2.數組長度計算方法
- 因爲數組在內存中佔用的字節數取決於其存儲的數據類型和數據的個數
數組在內存中佔用的總字節數:sizeof(數組名);
- 數組所佔用存儲空間 = 一個元素所佔用存儲空間 * 元素個數(數組長度)
- 所以計算數組長度可以使用如下方法
數組的長度 = 數組佔用的總字節數 / 數組元素佔用的字節數
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
printf("length = %d", length);
輸出結果: 4
---
##3.練習
- 正序輸出(遍歷)數組
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
- 逆序輸出(遍歷)數組
int ages[4] = {19, 22, 33, 13};
for (int i = 3; i >=0; i--) {
printf("ages[%d] = %d\n", i, ages[i]);
}
- 從鍵盤輸入數組長度,構建一個數組,然後再通過for循環從鍵 盤接收數字給數組初始化。並使用for循環輸出查看
數組的內存分配
本小節知識點:
- 【掌握】數組內部存儲細節
- 【理解】數組的地址
- 【理解】數組的越界問題
1.數組內部存儲細節
-
存儲方式:
- 1)計算機會給數組分配一塊連續的存儲空間
- 2)數組名代表數組的首地址,從首地址位置,依次存入數組的第1個、第2個....、第n個元素
- 3)每個元素佔用相同的字節數(取決於數組類型)
- 4)並且數組中元素之間的地址是連續。
-
示例
模擬該數組的內存存儲細節如下: int x[2]={1,2}; int ca[5]={'a','A','B','C','D'};
- 注意:字符在內存中是以對應ASCII值的二進制形式存儲的,而非上表的形式。 在這個例子中,數組x的地址爲它的首元素的地址0x08,數組ca的地址爲0x03。
2.數組的地址
- 在內存中,內存從大到小進行尋址,爲數組分配了存儲空間後,數組的元素自然的從上往下排列 存儲,整個數組的地址爲首元素的地址。
- 數組a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc5
- 因此a == &a[0],即第一個元素的地址就是整個數組的地址
3.數組的越界問題
- 數組越界導致的問題
- 約錯對象
- 程序崩潰
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:這句訪問到了不屬於cs1的內存
printf("cs1[0] = %d\n", cs1[0] );
輸出結果: 88
數組練習
1.設計一個函數int arrayMax(int a[], int count)找出數組元素的最大值
2.設計一個函數:int arraySum(int a[], int n),求一維數組a前n個數的和
3.寫一個函數,可以將一維整型數組中的元素逆序存放。比如本來是1,3,4,2,逆序存放就變成了:2,4,3,1
數組元素作爲函數參數
1.本小節知識點:
- 【掌握】數組元素作爲函數參數
- 【掌握】數組名作爲函數參數
- 【掌握】數組名作函數參數的注意點
- 數組可以作爲函數的參數使用,進行數據傳送。數組用作函數參數有兩種形式:
- 一種是把數組元素(下標變量)作爲實參使用
- 一種是把數組名作爲函數的形參和實參使用
1.數組元素作爲函數參數
-
數組元素就是下標變量,它與普通變量並無區別。 因此它作爲函數實參使用與普通變量是完全相 同的,在發生函數調用時,把作爲實參的數組元素的值傳送給形參,實現單向的值傳送。
-
數組的元素作爲函數實參,與同類型的簡單變量作爲實參一樣,是單向的值傳遞,即數組元素的值傳給形參,形參的改變不影響實參
void change(int val)// int val = number { val = 55; } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; printf("ages[0] = %d", ages[0]);// 1 change(ages[0]); printf("ages[0] = %d", ages[0]);// 1 }
- 用數組元素作函數參數不要求形參也必須是數組元素
2.數組名作爲函數參數
-
在C語言中,數組名除作爲變量的標識符之外,數組名還代表了該數組在內存中的起始地址, 因此,當數組名作函數參數時,實參與形參之間不是"值傳遞",而是"地址傳遞",實參數組名將 該數組的起始地址傳遞給形參數組,兩個數組共享一段內存單元,編譯系統不再爲形參數組分配 存儲單元。
-
數組的名字作爲函數實參,傳遞的是整個數組,即形參數組和實參數組完全等同,是存放在同一存儲空間的同一個數組。這樣形參數組修改時,實參數組也同時被修改了。形參數組的元素個數可以省略
void change2(int array[3])// int array = 0ffd1 { array[0] = 88; } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; printf("ages[0] = %d", ages[0]);// 1 change(ages[0]); printf("ages[0] = %d", ages[0]);// 88 }
3.數組名作函數參數的注意點
- 在函數形參表中,允許不給出形參數組的長度
void change2(int array[]) { array[0] = 88; }
- 形參數組和實參數組的類型必須一致,否則將引起錯誤。
void prtArray(double array[3]) // 錯誤寫法 { for (int i = 0; i < 3; i++) { printf("array[%d], %f", i, array[i]); } } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; prtArray(ages[0]); }
- 當數組名作爲函數參數時, 因爲自動轉換爲了指針類型,所以在函數中無法動態計算除數組的元素個數
void printArray(int array[]) { printf("printArray size = %lu\n", sizeof(array)); // 8 int length = sizeof(array)/ sizeof(int); // 2 printf("length = %d", length); }
冒泡排序
本小節知識點:
- 【瞭解】冒泡排序
- 【掌握】冒泡排序的步驟
- 【瞭解】練習
1.冒泡排序
-
冒泡排序(Bubble Sort,臺灣譯爲:泡沫排序或氣泡排序)是一種簡單的排序算法。它重複 地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列 的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來 是因爲越小的元素會經由交換慢慢“浮”到數列的頂端。
-
冒泡排序 分爲: 大數下沉 小數上浮
2.冒泡排序
- 1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
- 2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應 該會是最大的數。
- 3)針對所有的元素重複以上的步驟,除了最後一個。
-
4)持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
-
示例:
3.練習
- 輸入一組無序數據,使用冒泡排序法進行排序,並輸出。
選擇排序
本小節知識點:
- 【瞭解】選擇排序
- 【掌握】選擇排序的基本思想
- 【瞭解】練習
1.選擇排序
- 選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理如下。首先在未排序序列 中找到最小元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小元 素,然後放到排序序列末尾。以此類推,直到所有元素均排序完畢。
2.選擇排序的基本思想
-
第一趟排序在所有待排序的n個記錄中選出關鍵字最小的記錄,將它與數據表中的第一個記錄交 換位置,使關鍵字最小的記錄處於數據表的最前端;第二趟在剩下的n-1個記錄中再選出關鍵字最 小的記錄,將其與數據表中的第二個記錄交換位置,使關鍵字次小的記錄處於數據表的第二個位 置;重複這樣的操作,依次選出數據表中關鍵字第三小、第四小...的元素,將它們分別換到數據 表的第三、第四...個位置上。排序共進行n-1趟,最終可實現數據表的升序排列。
-
示例
3.練習
- 輸入一組無序數據,使用選擇排序法進行排序,並輸出。
折半查找
本小節知識點:
- 【掌握】基本思路
- 【掌握】實現步驟
- 【瞭解】練習
1.基本思路
-
在有序表中,取中間元素作爲比較對象,若給定值與中間元素的要查找的數相等,則查找成功; 若給定值小於中間元素的要查找的數,則在中間元素的左半區繼續查找;
-
若給定值大於中間元素的要查找的數,則在中間元素的右半區繼續查找。不斷重複上述查找過 程,直到查找成功,或所查找的區域無數據元素,查找失敗。
2.實現步驟
- 在有序表中,取中間元素作爲比較對象,若給定值與中間元素的要查找的數相等,則查找成功;
- 若給定值小於中間元素的要查找的數,則在中間元素的左半區繼續查找;
-
若給定值大於中間元素的要查找的數,則在中間元素的右半區繼續查找。不斷重複上述查找過 程,直到查找成功,或所查找的區域無數據元素,查找失敗。
-
示例:
3.練習
- 輸入一組有序數據,使用折半查找法查找一個數據,並輸出其位 置。
進制轉換查表法
void toBinary2(int num)
{
total(num, 1, 1);
}
void toOct2(int num)
{
total(num, 7, 3);
}
void toHex2(int num)
{
total(num, 15, 4);
}
void total(int num , int base, int offset)
{
// 1.定義表用於查詢結果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
// 2.定義保存結果的數組
char rs[32];
// 計算最大的角標位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int index = num & base;
rs[--pos] = cs[index];
num = num >> offset;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
printf("\n");
}
void toBinary(int num)
{
// 1.定義表用於查詢結果
char cs[] = {
'0', '1'
};
// 2.定義保存結果的數組
char rs[32];
// 計算最大的角標位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 1;
rs[--pos] = cs[result];
num = num >> 1;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void toOct(int num)
{
// 1.定義表用於查詢結果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7'
};
// 2.定義保存結果的數組
char rs[11];
// 計算最大的角標位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 7;
rs[--pos] = cs[result];
num = num >> 3;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void toHex(int num)
{
// 1.定義表用於查詢結果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
// 2.定義保存結果的數組
char rs[8];
// 3.定義存儲結果的索引
// 計算保存結果數組的元素個數
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 15;//12
//7 6
rs[--pos] = cs[result];//c 3
// pos--;
num = num >> 4;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void printHex(int num)
{
// 9 a b c d e f
for (int i = 0; i < 8; i++) {
int result = num & 15;
if (result > 9) {//10 11 1+
printf("%c", result - 10 + 'a');
}else{
printf("%d", result);
}
num = num >> 4;
}
}
void printOct(int num)
{
for (int i = 0; i < 11; i++) {
int result = num & 7;
printf("%d", result);
num = num >> 3;
}
}
void printBinary(int num)
{
// 要左移的位數
int temp = (sizeof(int) << 3) - 1;
while (temp >= 0) {
int result = (num >> temp) & 1;
printf("%d", result);
if (temp % 4 == 0) {
printf(" ");
}
temp--;
}
}