文章目錄
- 陣列
- 一維陣列
- 創建
- 定義
- 創建實體
- 初始化
- 使用
- 總結:
- 記憶體存盤
- 二維陣列
- 創建
- 初始化
- 省略
- 使用
- 記憶體存盤
- 理解方式
- 陣列越界問題
- 定義
- 陣列作函式引數
- 應用實體
- 陣列的應用實體1:三子棋
- 陣列的應用實體2:掃雷游戲
陣列
一維陣列
創建
定義
陣列是一組相同型別的元素的集合,那陣列的語法形式:
type_t arr_name [const_n]
//如:
int arr[10];
type_t指的是陣列元素的型別,
const_n指的是一個常量運算式,用來指定陣列的大小,
此時運行程式的話,系統會報一個警告:未初始化變數,打開除錯就會發現系統默認填入一些無意義的資料,

當然全域陣列的話,系統默認初始化為0;
int arr[10];// 0 0 ... 0
int main(){
return 0;
}
創建實體
//1.
int arr[10];
//2.
int count = 10;
int arr2[count];//這樣的創建陣列可不可以呢?
//3.
float arr3[20];//浮點型陣列
char ch[10];
陣列的創建必須要
[]使用常量,不能使用變數,(ps:雖然C99支持變長陣列,但一般用常數創建就已經夠用了)同樣,我雖然用const_n表示常量,但可千萬不要誤會為const修飾的變數哦,
為什么呢?
因為陣列控制不好容易越界訪問非法記憶體,用變數的話風險太大,所以一直以來都是用常量創建陣列的,
初始化
初始化,顧名思義,在創建陣列的同時給予一些合理的初識值,如:
int arr[10] = { 1,2,3 };//不完全初始化
這種是不完全初始化,剩余的元素默認是0
int arr2[] = { 1,2,3,4 };//利用初始化內容,指定陣列大小
這種是省略陣列的
const_n常量運算式由初始化內容指定陣列的大小
那下面這三個有什么不同呢?

第一種是用字串初始化陣列,字串有
\0作為結束標志,雖不算字串內容,但是可以說是字串與生俱來的,所以它也被初始化作為陣列內容,a b c \0第二種和第三種是一樣的,因為陣列元素型別是字符型,且字符
'b'的ACSII碼值是98,自動將98決議為字符,a b c
使用
陣列的訪問是通過下標來訪問的,默認下標是從0開始,通過下標參考運算子[]我們可以訪問到陣列元素,
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//陣列的下標是從0開始的0~9
int sz = sizeof(arr) / sizeof(arr[0]);//10
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
//1 2 3 4 5 6 7 8 9 10
對于
sizeof運算子,sizeof(arr),即sizeof+陣列名,指的是計算整個陣列的大小,算出來是40,然后sizeof(arr[0])是計算陣列首元素的大小為4,這樣一除就是元素個數啦,使用變數
sz,可以靈活的改變陣列的大小,就不用再更改回圈條件了,
總結:
- 陣列是通過下標訪問的,下標從0開始
- 陣列的大小可以通過計算得到
記憶體存盤

通過printf("&arr[%d]=%p\n", i,&arr[i]);這樣的陳述句我們可以看到該陣列在記憶體中的存盤情況,
很明顯的是,陣列在記憶體中是連續存放的,

右邊是十六進制的記憶體編號,可以看見每一個元素之間都相差4個位元組,而一個整型元素正好占4個位元組,
所以陣列在記憶體中是連續存放的,隨著陣列下標的增長,地址也在增長,這也正是為什么指標變數+1,可以訪問到下一個陣列元素,
所以陣列的本質是什么?
一組記憶體中連續存放的相同型別的元素,
二維陣列
創建
type_t arr_name[const_n][const_n]
//
int arr[3][5];//3行5列
char ch[4][7];//4行5列
double arr2[2][4]//2行4列
如上述代碼所示:二維陣列的語法結構就是,型別+陣列名+[行][列],

如圖所示,二維陣列在理解上就是這樣的3行5列類似于表格的東西,就像線性代數里的矩陣,矩陣的定義就是一組陣列成數表,
初始化
//1.
int arr1[3][5] = { 1,2,3,4,5,6,7,8,9,10,11 };
//2.
int arr2[3][5] = { {1,2},{3,4},{5,6,7} };
- 第一種初始化,先一行一行填入,第一行是
1 2 3 4 5,第二行是6 7 8 9 10,第三行不夠就補零11 0 0 0 0,- 第二種的話,把每一行看成一個一維陣列,不夠的話還是補零,即第一行
1 2 0 0 0,第二行3 4 0 0 0,第三行5 6 7 0 0,

char ch1[2][4] = { 'a','b' };
char ch2[2][4] = { {'a'},{'b'} };
char ch3[3][4] = { "abc","def","gh" };
當然用字串去初始化二維陣列的話,也是需要注意\0的問題,
第一行:
a b c \0;第二行:d e f \0;第三行:g h \0 0
省略
int arr2[][5] = { {1,2},{3,4},{5,6,7} };
像這樣省略行可以,但是不能省略列,
行數可以根據初始化內容來規定,但如果列省略了就會造成歧義,
當然,省略必須在已經初始化的前提之下,不然行和列一概不知,怎么分配空間呢?
使用
當然二維陣列同樣是用下標訪問陣列內容的,也是從0開始,如:

我們要去訪問這個二維陣列的話,我們當然是用兩次回圈遍歷這個陣列,

記憶體存盤
當然我們也可以用同樣的辦法列印出每個元素的地址,如:

- 我們還是能發現每一個元素都是在記憶體中連續存放的,
這樣的話,二維陣列在記憶體中的存盤形式便是大家想象中的二維的形式,把每一行理解為一個一維陣列,這樣的話二維陣列在記憶體中的存盤形式還是一維的,如下圖的對比:

- 從這里我們也可以理解到,二維陣列的初始化里,為什么可以省略行不能省略列,
把行省略了,但是我們知道列,一個一個填滿就是了,能填到多少行就有多少行,
理解方式
- 對于二維陣列,我們可以理解為每一行為一個元素的一維陣列,該一維陣列的每一個元素又是一個一維陣列,
如陣列
arr[3][5],是有3個元素的一維陣列,每個元素是一個有5個元素的一維陣列,
指向二維陣列的指標+1,指向的是下一行,
對于二維陣列在記憶體存盤形式的理解還是很重要的,有了這樣的思想,我們就可以通過指標遍歷得到陣列元素,如:
int arr[3][5] = { {1,2,3},{4,5,6},{7,8} };
int* p = &arr[0][0];
for (int i = 0; i < 15; i++)
{
printf("%d ", *p++);//1 2 3 0 0 4 5 6 0 0 7 8 0 0 0
}
陣列越界問題
定義
陣列通過下標訪問,那么下標也就可以控制陣列的訪問范圍,在陣列前后進行訪問的話,就是非法訪問記憶體,即陣列越界,
//1 2 3 4 5 -858993460
int arr[5] = { 1,2,3,4,5 };
for (int i = 0; i <= 5; i++)//越界訪問到第6個
{
printf("%d ", arr[i]);
}
陣列越界訪問到最后一個元素之后的一塊記憶體,這就屬于越界訪問,-858993460是vs2019自動生成的隨機值,
一般編譯器是不會去檢查陣列越界訪問的情況(vs2019太先進),所以我們就要有意識的主動檢查,如果編譯器提示這樣的錯誤資訊,那么一般就是陣列越界了:

陣列作函式引數
在寫代碼時,我們經常會將陣列作為引數,比如接下來的兩個應用實體,那么我們這里以冒泡排序的實作作為案例,
在寫代碼時,我們經常會將陣列作為引數,比如接下來的兩個應用實體,那么我們這里以冒泡排序的實作作為案例,
排序演算法一般有四種:冒泡排序、選擇排序、插入排序和快速排序,
冒泡排序的核心思想:兩兩相鄰的元素進行比較,
- 一趟冒泡排序搞定一個數字,讓其來到最終的位置上,
-
n
n
n 個元素,則總共需要
n
?
1
n-1
n?1 趟冒泡排序,每一趟排序需要進行
n
?
1
?
i
n-1-i
n?1?i 次判斷大小,如分析圖所示:

void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *arr++);
}
}
void Sort(int arr[],int sz)//int* arr
//陣列作形參本質是指標
{
//int sz = sizeof(arr) / sizeof(arr[0]);//err//用指標的sizeof值除以另一個值 = 4 / 4 = 1
for (int i = 0; i < sz - 1; i++)//n-1趟
{
for (int j = 0; j < sz - 1 - i; j++)//n-1-i次
{
if (arr[j] > arr[j + 1])//目標升序
{
//交換
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 1,4,6,3,7,9,3,2,8,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
//陣列名單獨放在sizeof中,表示整個陣列
//排序
Sort(arr,sz);
//列印
Print(arr,sz);
return 0;
}
- 定義陣列作形參時,本質上是指標,
void Sort(int *arr,int sz)本質上就是void Sort(int arr[],int sz)所以
Sort()函式內,sizeof(arr)也算的就是指標arr的大小,所以只能傳參進去,
-
陣列名
arr何時代表整個陣列何時代表陣列首元素地址呢?-
代表整個陣列的情況:
單獨放在
sizeof運算子內部時,如sizeof(arr);,寫出
&arr時,代表的是整個陣列,但表面仍為首元素地址, -
代表首元素地址的情況:
除上面兩以外其他都是代表首元素的地址,
-
應用實體
筆者實在沒時間寫兩個應用實體的博客,所以在此將思維導圖奉上,一般照著思維導圖寫就沒問題了,感謝李姐和支持~
陣列的應用實體1:三子棋

陣列的應用實體2:掃雷游戲

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/291242.html
標籤:其他
下一篇:CSS畫心形的三種方法,超級簡單
