一篇文章帶你入門C語言:數(shù)組
數(shù)組
一維數(shù)組
創(chuàng)建 定義
數(shù)組是一組相同類型的元素的集合。那數(shù)組的語法形式:
type_t arr_name [const_n] //如: int arr[10];
type_t 指的是數(shù)組元素的類型。
const_n 指的是一個常量表達式,用來指定數(shù)組的大小。
此時運行程序的話,系統(tǒng)會報一個警告:未初始化變量。打開調(diào)試就會發(fā)現(xiàn)系統(tǒng)默認填入一些無意義的數(shù)據(jù)。
當然全局數(shù)組的話,系統(tǒng)默認初始化為0;
int arr[10];// 0 0 ... 0 int main(){ return 0; }
創(chuàng)建實例
//1. int arr[10]; //2. int count = 10; int arr2[count];//這樣的創(chuàng)建數(shù)組可不可以呢? //3. float arr3[20];//浮點型數(shù)組 char ch[10];
數(shù)組的創(chuàng)建必須要[]使用常量,不能使用變量。(ps:雖然C99支持變長數(shù)組,但一般用常數(shù)創(chuàng)建就已經(jīng)夠用了)同樣,我雖然用const_n表示常量,但可千萬不要誤會為const修飾的變量哦。
為什么呢?
因為數(shù)組控制不好容易越界訪問非法內(nèi)存,用變量的話風險太大,所以一直以來都是用常量創(chuàng)建數(shù)組的。
初始化
初始化,顧名思義,在創(chuàng)建數(shù)組的同時給予一些合理的初識值。如:
int arr[10] = { 1,2,3 };//不完全初始化
這種是不完全初始化,剩余的元素默認是0
int arr2[] = { 1,2,3,4 };//利用初始化內(nèi)容,指定數(shù)組大小
這種是省略數(shù)組的const_n常量表達式
由初始化內(nèi)容指定數(shù)組的大小
那下面這三個有什么不同呢?
第一種是用字符串初始化數(shù)組,字符串有\(zhòng)0作為結(jié)束標志,雖不算字符串內(nèi)容,但是可以說是字符串與生俱來的,所以它也被初始化作為數(shù)組內(nèi)容。a b c \0
第二種和第三種是一樣的,因為數(shù)組元素類型是字符型,且字符'b'的ACSII碼值是98,自動將98解析為字符。a b c
使用
數(shù)組的訪問是通過下標來訪問的,默認下標是從0開始。通過下標引用操作符[]我們可以訪問到數(shù)組元素。
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //數(shù)組的下標是從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+數(shù)組名,指的是計算整個數(shù)組的大小,算出來是40,然后sizeof(arr[0])是計算數(shù)組首元素的大小為4,這樣一除就是元素個數(shù)啦。
使用變量sz,可以靈活的改變數(shù)組的大小,就不用再更改循環(huán)條件了。
總結(jié):
- 數(shù)組是通過下標訪問的,下標從0開始
- 數(shù)組的大小可以通過計算得到
內(nèi)存存儲
通過printf("&arr[%d]=%p\n", i,&arr[i]);這樣的語句我們可以看到該數(shù)組在內(nèi)存中的存儲情況。
很明顯的是,數(shù)組在內(nèi)存中是連續(xù)存放的。
右邊是十六進制的內(nèi)存編號,可以看見每一個元素之間都相差4個字節(jié),而一個整型元素正好占4個字節(jié)。
所以數(shù)組在內(nèi)存中是連續(xù)存放的,隨著數(shù)組下標的增長,地址也在增長,這也正是為什么指針變量+1,可以訪問到下一個數(shù)組元素。
所以數(shù)組的本質(zhì)是什么?
一組內(nèi)存中連續(xù)存放的相同類型的元素。
二維數(shù)組
創(chuàng)建
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列
如上述代碼所示:二維數(shù)組的語法結(jié)構(gòu)就是,類型+數(shù)組名+[行][列]。
如圖所示,二維數(shù)組在理解上就是這樣的3行5列類似于表格的東西。就像線性代數(shù)里的矩陣,矩陣的定義就是一組數(shù)組成數(shù)表。
初始化
//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 。第二種的話,把每一行看成一個一維數(shù)組,不夠的話還是補零,即第一行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" };
當然用字符串去初始化二維數(shù)組的話,也是需要注意\0的問題。
第一行:a b c \0;第二行:d e f \0;第三行:g h \0 0
省略
int arr2[][5] = { {1,2},{3,4},{5,6,7} };
像這樣省略行可以,但是不能省略列。
行數(shù)可以根據(jù)初始化內(nèi)容來規(guī)定,但如果列省略了就會造成歧義。
當然,省略必須在已經(jīng)初始化的前提之下,不然行和列一概不知,怎么分配空間呢?
使用
當然二維數(shù)組同樣是用下標訪問數(shù)組內(nèi)容的,也是從0開始。如:
我們要去訪問這個二維數(shù)組的話,我們當然是用兩次循環(huán)遍歷這個數(shù)組。
內(nèi)存存儲
當然我們也可以用同樣的辦法打印出每個元素的地址,如:
我們還是能發(fā)現(xiàn)每一個元素都是在內(nèi)存中連續(xù)存放的。
這樣的話,二維數(shù)組在內(nèi)存中的存儲形式便是大家想象中的二維的形式,把每一行理解為一個一維數(shù)組,這樣的話二維數(shù)組在內(nèi)存中的存儲形式還是一維的。如下圖的對比:
從這里我們也可以理解到,二維數(shù)組的初始化里,為什么可以省略行不能省略列。
把行省略了,但是我們知道列,一個一個填滿就是了,能填到多少行就有多少行。
理解方式
對于二維數(shù)組,我們可以理解為每一行為一個元素的一維數(shù)組,該一維數(shù)組的每一個元素又是一個一維數(shù)組。
如數(shù)組arr[3][5] ,是有3個元素的一維數(shù)組,每個元素是一個有5個元素的一維數(shù)組。
指向二維數(shù)組的指針+1,指向的是下一行。
對于二維數(shù)組在內(nèi)存存儲形式的理解還是很重要的,有了這樣的思想,我們就可以通過指針遍歷得到數(shù)組元素,如:
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 }
數(shù)組越界問題
定義
數(shù)組通過下標訪問,那么下標也就可以控制數(shù)組的訪問范圍。在數(shù)組前后進行訪問的話,就是非法訪問內(nèi)存,即數(shù)組越界。
//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]); }
數(shù)組越界訪問到最后一個元素之后的一塊內(nèi)存,這就屬于越界訪問,-858993460是vs2019自動生成的隨機值。
一般編譯器是不會去檢查數(shù)組越界訪問的情況(vs2019太先進),所以我們就要有意識的主動檢查。如果編譯器提示這樣的錯誤信息,那么一般就是數(shù)組越界了:
數(shù)組作函數(shù)參數(shù)
在寫代碼時,我們經(jīng)常會將數(shù)組作為參數(shù),比如接下來的兩個應用實例,那么我們這里以冒泡排序的實現(xiàn)作為案例。
排序算法一般有四種:冒泡排序、選擇排序、插入排序和快速排序。
冒泡排序的核心思想:兩兩相鄰的元素進行比較。
- 一趟冒泡排序搞定一個數(shù)字,讓其來到最終的位置上。
- 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 //數(shù)組作形參本質(zhì)是指針 { //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]); //數(shù)組名單獨放在sizeof中,表示整個數(shù)組 //排序 Sort(arr,sz); //打印 Print(arr,sz); return 0; }
1.定義數(shù)組作形參時,本質(zhì)上是指針。
void Sort(int *arr,int sz)本質(zhì)上就是void Sort(int arr[],int sz)
所以Sort()函數(shù)內(nèi),sizeof(arr)也算的就是指針arr的大小,所以只能傳參進去。
2.數(shù)組名arr何時代表整個數(shù)組何時代表數(shù)組首元素地址呢?
- 代表整個數(shù)組的情況:
單獨放在sizeof操作符內(nèi)部時,如sizeof(arr); 。
寫出&arr時,代表的是整個數(shù)組,但表面仍為首元素地址。
- 代表首元素地址的情況:
除上面兩以外其他都是代表首元素的地址。
應用實例
筆者實在沒時間寫兩個應用實例的博客,所以在此將思維導圖奉上,一般照著思維導圖寫就沒問題了。感謝李姐和支持~
數(shù)組的應用實例1:三子棋
數(shù)組的應用實例2:掃雷游戲
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
makefile如何調(diào)用靜態(tài)庫的方法實現(xiàn)
這篇文章主要介紹了makefile如何調(diào)用靜態(tài)庫的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12利用簡潔的C語言代碼解決跳臺階問題與約瑟夫環(huán)問題
這篇文章主要介紹了利用簡潔的C語言代碼解決跳臺階問題與約瑟夫環(huán)問題的方法,跳臺階問題與約瑟夫環(huán)問題是常見的基礎(chǔ)算法題目,需要的朋友可以參考下2016-02-02C++讀取訪問權(quán)限沖突引發(fā)異常問題的原因分析
C語言是一門通用計算機編程語言,廣泛應用于底層開發(fā),最近在用C++寫代碼時經(jīng)常會遇到“引發(fā)了異常: 讀取訪問權(quán)限沖突,所以這篇文章主要給大家介紹了關(guān)于C++讀取訪問權(quán)限沖突引發(fā)異常問題的相關(guān)資料,需要的朋友可以參考下2021-07-07