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