C語言數(shù)組超詳細(xì)講解上
前言
本文主要介紹數(shù)組相關(guān)的內(nèi)容,主要內(nèi)容包括:
- 一維數(shù)組的創(chuàng)建和初始化
- 一維數(shù)組的使用
- 一維數(shù)組在內(nèi)存中的存儲
- 二維數(shù)組的創(chuàng)建和初始化
- 二維數(shù)組的使用
- 二維數(shù)組在內(nèi)存中的存儲
- 數(shù)組越界
- 數(shù)組作為函數(shù)參數(shù)
1、一維數(shù)組的創(chuàng)建和初始化
1.1 一維數(shù)組的創(chuàng)建
數(shù)組是一組相同類型元素的集合,數(shù)組的創(chuàng)建方式:
//type_t 是指數(shù)組的元素類型 //const_n 是一個常量表達(dá)式,用來指定數(shù)組的大小 type_t arr_name [const_n]; //代碼1 int arr1[10]; //代碼2 int count = 10; int arr2[count];//數(shù)組不要放變量 //代碼3 char arr3[10]; float arr4[1]; double arr5[20];
注:數(shù)組創(chuàng)建,在C99標(biāo)準(zhǔn)之前, [ ] 中要給一個常量才可以,不能使用變量。在C99標(biāo)準(zhǔn)支持了變長數(shù)組的概念。
1.2 一維數(shù)組的初始化
數(shù)組的初始化是指,在創(chuàng)建數(shù)組的同時給數(shù)組的內(nèi)容一些合理初始值(初始化)
int arr[10] = {0}; int arr1[10] = {1,2,3}; int arr2[] = {1,2,3,4}; int arr3[5] = {1,2,3,4,5}; char arr4[3] = {'a',98, 'c'};//字符串也是通過數(shù)組定義的 char arr5[] = {'a','b','c'};//字符串也是通過數(shù)組定義的 char arr6[] = "abcdef";//字符串也是通過數(shù)組定義的
數(shù)組在創(chuàng)建的時候如果想不指定數(shù)組的確定的大小就得初始化。數(shù)組的元素個數(shù)根據(jù)初始化的內(nèi)容來確定。
仔細(xì)對比下面5個數(shù)組:
char arr1[] = "abc"; char arr2[3] = { 'a','b','c' }; char arr3[] = { 'a','b','c' }; char arr4[4] = { 'a','b','c' }; char arr5[] = { 'a','b','c','\0' }; printf("%s\n", arr1); printf("%s\n", arr2); printf("%s\n", arr3); printf("%s\n", arr4); printf("%s\n", arr5); printf("\n"); printf("%d\n", sizeof(arr1)); printf("%d\n", sizeof(arr2)); printf("%d\n", sizeof(arr3)); printf("%d\n", sizeof(arr4)); printf("%d\n", sizeof(arr5));
對比下面兩圖可知,字符串以字符 ‘\0’ 為結(jié)尾:
- arr1 用雙引號存儲字符串時,末尾有隱藏的 ‘\0’ ,字符串長度為4
- arr4 規(guī)定了字符串的長度,末尾也有隱藏的 ‘\0’ ,字符串長度為4
- arr5 規(guī)直接末尾添加了 ‘\0’ ,字符串長度為4
- arr3 和 arr4都是只有三個字符,字符串長度為3,但是末尾沒有 ‘\0’ ,字符串沒有結(jié)束,打印出來后面是亂碼的
下面兩圖的結(jié)果能清楚的表示,上面5中定義的區(qū)別,因此,推薦使用數(shù)組arr1的方式定義字符串。
1.3 一維數(shù)組的使用
對于數(shù)組的使用我們之前介紹了一個操作符: [ ] ,下標(biāo)引用操作符。它其實就數(shù)組訪問的操作符。
int main() { int arr[10] = {0};//數(shù)組的不完全初始化 //計算數(shù)組的元素個數(shù)= 整個數(shù)組的大小/數(shù)組首元素的大小 int sz = sizeof(arr)/sizeof(arr[0]); //對數(shù)組內(nèi)容賦值,數(shù)組是使用下標(biāo)來訪問的,下標(biāo)從0開始。所以: int i = 0;//做下標(biāo) //for(i=0; i<sz; i++)//這樣也行 for(i=0; i<10; i++) { arr[i] = i;//給數(shù)組元素初始化 } //輸出數(shù)組的內(nèi)容 for(i=0; i<10; ++i) { printf("%d ", arr[i]);//打印數(shù)組 } return 0; }
數(shù)組是使用下標(biāo)來訪問的,下標(biāo)是從0開始。 數(shù)組的大小可以通過計算得到的。
1.4 一維數(shù)組在內(nèi)存中的存儲
接下來探討數(shù)組在內(nèi)存中的存儲
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //打印數(shù)組中的每個元素的地址 int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); int* p = &arr[0];//元素首地址 for (int i = 0; i < sz; i++) { printf("%d ", *(p++));//地址加1 } return 0; }
兩種打印數(shù)組的方式都可以,結(jié)果相同:
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //打印數(shù)組中的每個元素的地址 int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int* p = &arr[0];//元素首地址 for (int i = 0; i < sz; i++) {//打印地址 printf("&arr[%d]=%p <==> %p\n", i, &arr[i], p++); } return 0; }
輸出結(jié)果見下圖,隨著數(shù)組下標(biāo)的增長,元素的地址,也在有規(guī)律的遞增。整形數(shù)組每個元素占4個字節(jié),共10個元素,總共40個字節(jié)。所以相鄰元素的地址依次遞增4個字節(jié)。
由此可以得出結(jié)論:數(shù)組在內(nèi)存中是連續(xù)存放的。
2、二維數(shù)組的創(chuàng)建和初始化
2.1 二維數(shù)組的創(chuàng)建
//數(shù)組創(chuàng)建 int arr[3][4]; char arr[3][5]; double arr[2][4];
2.2 二維數(shù)組的初始化
//數(shù)組初始化 int arr[3][4] = {1,2,3,4}; int arr[3][4] = {{1,2},{4,5}}; int arr[][4] = {{2,3},{4,5}};//二維數(shù)組如果有初始化,行可以省略,列不能省略 int arr1[][5] = { 1,2,3,4,5,6 };//不完全初始化 int arr2[][5] = { {1,2},{3,4},{5,6} }; char ch[5][7]; int arr[4][5] = { 0 };
2.3 二維數(shù)組的使用
二維數(shù)組的使用也是通過下標(biāo)的方式
int main() { int arr2[][5] = { {1,2},{3,4},{5,6} }; for (int i = 0; i < sizeof(arr2) / sizeof(arr2[0]); i++)//打印二維數(shù)組 { //int j = 0; for (int j = 0; j < sizeof(arr2[0]) / sizeof(arr2[0][0]); j++) { printf("%d ", arr2[i][j]); } printf("\n"); } return 0; }
2.4 二維數(shù)組在內(nèi)存中的存儲
像一維數(shù)組一樣,打印二維數(shù)組的每個元素
int main() { int arr[3][5] = { {1,2},{3,4},{5,6} }; for (int i = 0; i < 3; i++) { int j = 0; for ( j = 0; j < 5; j++) { printf("&arr[%d]{%d]=%p\n", i, j, &arr[i][j]);//地址是連續(xù)的 } } return 0; }
二維數(shù)組的地址在內(nèi)存中也是連續(xù)的,相鄰元素依次相差4個字節(jié),&arr[0][3]與&arr[1][0]是相連的。
3、數(shù)組越界
- 數(shù)組的下標(biāo)是有范圍限制的
- 數(shù)組的下規(guī)定是從0開始的,如果數(shù)組有n個元素,最后一個元素的下標(biāo)就是n-1。所以數(shù)組的下標(biāo)如果小于0,或者大于n-1,就是數(shù)組越界訪問了,超出了數(shù)組合法空間的訪問
- C語言本身是不做數(shù)組下標(biāo)的越界檢查,編譯器也不一定報錯,但是編譯器不報錯,并不意味著程序就是正確的,所以程序員寫代碼時,最好自己做越界的檢查
- 二維數(shù)組的行和列也可能存在越界。
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int i = 0; for(i=0; i<=10; i++) { printf("%d\n", arr[i]);//當(dāng)i等于10的時候,越界訪問了 } return 0; }
4、數(shù)組作為函數(shù)參數(shù)
往往我們在寫代碼的時候,會將數(shù)組作為參數(shù)傳個函數(shù),比如:要實現(xiàn)一個冒泡排序函數(shù),將一個整形數(shù)組排序
4.1 冒泡排序函數(shù)的錯誤設(shè)計
//方法1: void bubble_sort(int arr[])//接受數(shù)組 {//計算數(shù)組的長度 int sz = sizeof(arr)/sizeof(arr[0]); int i = 0; for(i=0; i<sz-1; i++) { int j = 0; for(j=0; j<sz-i-1; j++) { if(arr[j] > arr[j+1]) {//前者比后者大,則兩者交換 int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } } int main() { int arr[] = {3,1,7,5,8,9,0,2,4,6}; bubble_sort(arr);//將數(shù)組作為參數(shù)傳遞 for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++) { printf("%d ", arr[i]); } return 0; }
結(jié)果見下圖,沒有達(dá)到預(yù)期的排序效果。
按下F10進(jìn)入調(diào)試界面,按F11進(jìn)行單步調(diào)試,會發(fā)現(xiàn)主函數(shù)中的數(shù)組是10個元素,這是自己定義的,沒有問題。
但是將數(shù)組當(dāng)作參數(shù)傳遞給函數(shù)bubble_sort會發(fā)現(xiàn),函數(shù)接受的數(shù)組只包含首元素3。
因此計算數(shù)組長度sizeof(arr)時,結(jié)果長度是4,不再是原來主函數(shù)里的40了。
調(diào)試之后可以看到 bubble_sort 函數(shù)內(nèi)部的 sz ,是1。
所以數(shù)組作為函數(shù)參數(shù)的時候,不是把整個數(shù)組的傳遞過去。
這種情況之前在C語言函數(shù)超詳細(xì)講解上篇中 4.3.3 二分查找中就具體分析過。
4.2 數(shù)組名是什么?
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%p\n", arr);//首元素的地址 printf("%p\n", arr+1);//+1指向第二個元素 printf("%p\n", &arr[0]);//首元素的地址 printf("%p\n", &arr[0]+1);+1//指向第二個元素 printf("%p\n", &arr);//整個數(shù)組的地址 printf("%p\n", &arr+1); return 0; }
運行結(jié)果見下圖:
- 數(shù)組名就是首元素的地址,用指針接收
- 對數(shù)組名求地址,是整個數(shù)組的地址
- &arr的地址和 &arr+1 的地址相差28,這是16進(jìn)制,轉(zhuǎn)化為2進(jìn)制就是 40了,這正是數(shù)組的長度,包含10個元素
4.3 對數(shù)組名的用法進(jìn)行總結(jié)
- sizeof(數(shù)組名),計算整個數(shù)組的大小,sizeof內(nèi)部單獨放一個數(shù)組名,數(shù)組名表示整個數(shù)組。
- &數(shù)組名,取出的是數(shù)組的地址。&數(shù)組名,數(shù)組名表示整個數(shù)組。
- 除此1,2兩種情況之外,所有的數(shù)組名都表示數(shù)組首元素的地址。
4.4 冒泡排序函數(shù)的正確設(shè)計
上述代碼當(dāng)數(shù)組傳參的時候,實際上只是把數(shù)組的首元素的地址傳遞過去了。
所以即使在函數(shù)參數(shù)部分寫成數(shù)組的形式: int arr[] 表示的依然是一個指針: int *arr 。因為地址可以用指針就收。
那么,函數(shù)內(nèi)部的 sizeof(arr) 結(jié)果是4。數(shù)組長度應(yīng)該放在主函數(shù)中進(jìn)行計算。
下面對4.2 進(jìn)行改進(jìn),有時候數(shù)組元素本身就是按一定順序排好的,只需第一輪判斷即可:
void bubble_sort(int* arr, int sz) { //排序坐外面的大循環(huán)次數(shù) int i = 0; for ( i = 0; i < sz-1; i++) { int flag = 1;//狀態(tài)機(jī)標(biāo)志位,代表數(shù)組本身元素就是從小到大排序的 int j = 0; for ( j = 0; j < sz-1-i; j++) { if (arr[j]>arr[j+1]) { flag = 0;//只要有一個地方需要排序,就置零 int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } if (1==flag) {//第一輪排序結(jié)果都是1,說明沒有地方需要排序 break;//直接跳出后面的循環(huán),不需要再排序了 } } } int main() { //int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr[] = { 3,1,2,4,5,6,8,9,7,10 }; //寫一個冒泡排序的函數(shù), //arr表示首元素的地址, &arr[0],也是首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz); int i = 0; for ( i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
總結(jié)
本文是對數(shù)組相關(guān)知識點的學(xué)習(xí),下一篇將通過完成三子棋游戲鞏固前面所學(xué)的知識點。
到此這篇關(guān)于C語言數(shù)組超詳細(xì)講解上的文章就介紹到這了,更多相關(guān)C語言 數(shù)組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于數(shù)據(jù)結(jié)構(gòu)單向鏈表的各種操作
這篇文章主要介紹了關(guān)于數(shù)據(jù)結(jié)構(gòu)單向鏈表的各種操作,關(guān)于數(shù)據(jù)結(jié)構(gòu)鏈表的操作一般涉及的就是增刪改查,下面將關(guān)于無空頭鏈表展開介紹,需要的朋友可以參考下2023-04-04C語言實現(xiàn)簡單學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)簡單學(xué)生管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01詳解C語言中free()函數(shù)與getpagesize()函數(shù)的使用
這篇文章主要介紹了詳解C語言中free()函數(shù)與getpagesize()函數(shù)的使用,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-08-08