嘉明的C学习之Day8--数组
知識點回顧
整形 int 大小:4個字節
浮點型 float 大小:4個字節
字符型 char 大小:1個字節
數組
數組是什么?
舉個例子:
小明是一個球鞋愛好者且是一名編程愛好者。為了收藏鞋子,他買了很多個獨立的小鞋箱子放置他心愛的球鞋。
他把鞋箱放成一排(一維數組),其中一排有十個鞋箱(數組表示為a[10])。
有一天小明叫媽媽幫他拿鞋子,媽媽問:“你那么多鞋,我怎么知道拿那一對?”
小明說:“從左到右的第三個箱子的鞋子。”(a[2])
于是媽媽便很快找到了小明的鞋子。
隨著時間的流逝,小明的鞋子越來越多鞋箱也越來越多。因為寬度不夠,于是小明選擇向上疊加疊到了三行,且每一列的排數箱子數量都是10個(二維數組)(a[3][10])。
又有一天小明叫媽媽幫他拿鞋子,媽媽問:“你的鞋子堆的到處都是,我怎么知道拿那一對?”
小明說:“拿a[1][4](第二行第五列)那對吧。”
媽媽頓了下問:“啥?”于是拿了第一行第四列的鞋子給小明
小明說:“不是這對啊,是a[1][4]啊,第二行第五列啊。”
媽媽說:“那不是a[2][5]嗎,什么a[1][4]?”
小明說:“可是編程里面是從0開始的呀。”
媽媽看了看滿屋的鞋子,憤怒的舉起拿起掃把:“啊?什么編程?老娘的規矩就是規矩!買那么多鞋子浪費多少💴,打掃衛生時天天堵在那里看到都煩!”
小明媽媽拿起了掃把展示失傳的少林棍法。小明就這樣度過了快樂的一天
尬!
看了上面的例子是否感到尷尬的同時,也理解了數組是什么呢?
數組就是為了用一個符號來訪問多個元素
一維數組
一維數組就是剛剛例子中的一排鞋盒
例如
int a[5],表示定義了有5個元素的一維數組
如下:
可以看到我們查看監視的時候只有a[0]到a[4]。這就是為什么上面的故事要這樣編(哈哈哈哈就是皮)
內存問題
監視里還有個數組a的sizeof(字節大小)是20因為一個int為4 有5個.所以4*5=20。
其實在數組定義下來之后,大小就已經確認了
如果定義了數組可以存儲5個,只賦值兩個會怎么樣,如下圖一樣
其他沒賦值的都是0,而字節大小還是20。
常見的錯誤:
數組訪問越界錯誤
比如我們定義了一個一維數組a[5],如果我們為a[5]或者a[6]賦值是會提示如上錯誤的。因為我們已經越界了
如下程序:
#include<stdio.h> int main() {int i = 5;int j = 10;int a[5]={ 1,2 ,3,4,5};a[5] = 10; }
前提摘要:
我們微軟在編譯時不同的變量是有8個字節的保護間隔的,注意只是在微軟系統上。
例如上面這兩張圖,我一開始定義 i 和 j 就是為了說明這一點。可以看到他們地址之間間隔了8。
再來回到正題
我們調試的時候看到了內存里突然多出一塊紅色的列。這個值正好就是10。這個就是越界的值,但是因為保護,它還a[4]地址的4個字節之后顯示。
那么訪問越界會導致什么問題呢?
在我們正常使用中,訪問越界有些是不會提示的。如果它正常執行就會導致破壞其他變量的數據。
我們現在加一個 a[7] =20,很明顯這訪問越界了。但是我們繼續執行它
在這之前我們先記住j的值是20
輸出結果
但是我們最后所得到的值是22?這是為什么呢?
我們回看一下內存,如下圖
可以看到j的值已經變成22了。
這是因為j本來就在數組a的8個字節之后(因為定義的是5個元素,根據微軟的編譯規則有8個字節的保護區間,所以就是在a[4]地址后的8個字節)而a[7]就剛好在a[4]的后8個字節與變量j的地址重合,進而就會取代j的值。這就是越界的危害
其實在定義a[5]的時候已經訪問越界了,訪問量不屬于自己的空間,但是在保護區間內。一旦超過了保護區間就會對數據造成破壞。
題外話:
在語言層面,例如java、python這種語言一般是不會出現越界的情況的,因為沒有指針。而C難就難在指針,而且因為C中的內存是由我們自己來控制,所以使用C十分的高效和精確。但是問題就是對新手十分不友好,容易出BUG。但是對于老手來說C和C++無疑是最高效的。語言沒有好與壞,只有符不符合自己,我也用過java做過一些輕量級開的項目發覺得很方便、也學過一點python覺得語法很簡單庫的功能很強大,但是C是最接近底層的這就是為什么考研大部分都C來考這樣學習下來可以讓我們明白很多原理的東西,例如計組、操作系統等等。總之各有各的好處,學語言一定要符合自己才是王道!
數組傳遞問題
舉個例子,如下程序
#include<stdio.h> void print(int a[]) {for (int i = 0; sizeof(a) / sizeof(a) > i; i++) {printf("a[%d]=%d\n", i, a[i]);} } int main() {int a[5] = { 1,2 ,3,4,5 };print(a); }我們定義一個打印的方法,其目的就是打印數組a所有的元素,比如運行之后輸出a[0] =1 a[1]=2這樣的結果。
那么有的同學就會想這個簡單不直接用sizeof(a)算出數組的總字節數除以sizeof(int)int類型的字節大小就可以算出總數組的長度了嗎?而且還靈活多變
那么我們運行一下看看結果是不是這樣的。
結果好像有點跟我們想的不一樣,沒有輸出5各元素的值只輸出了第一個a[0]的值。
那為什么會這樣呢?這也是我們接下來要講的重點。
老規矩,在C中遇到與我們預期不符的事情,第一件事情就是debug看監視和內存
可以看到在print方法中的sizeof(a)是4。并不是原來的20,這是為什么呢?
所以下面我們要記住這一句話!
數組在傳遞的時候,傳遞的只是起始地址,元素個數是傳遞不過去的
根據這句話我們可以分析出,這里print方法里的數組變量a其實是由主方法main中數組a傳過來的初始地址值,后續在方法中訪問也是根據地址訪問數組中的元素的。元素的個數并未傳過去,因此它的sizeof只是一個整形的大小(4個字節)。
就好比小明給小紅很多💴,但只小明沒有直接給小紅💴而是給了小紅一張支票,因此小紅手里也只有一張支票,需要去銀行取錢。
那要怎么才能讓print方法中實現我們預期的功能呢?
其實很簡單,我們只需要在print方法中再添加一個參數表示數組從長度(元素總數)就可以了。
那么我們引申一下問題,在print方法中是否可以改變主方法main中數組a的某個元素的值呢?
注意:方法中的參數名稱給主方法main中變量名不需要一致,因為只是傳遞參數只要類型一樣即可
比如上述代碼中print方法中的b[4]=20可以改變a[4]的值嗎?
從輸出結果看來,是可以的。
因為主方法main中的a傳過去的是初始地址,print中根據地址肯定可以找出a[4]因此也可以改變它的值。
還是剛剛舉的例子,小紅拿到支票后去銀行取錢,她看到了有一張錢不太順眼,當然可以把它換了。
字符數組
字符數組其實跟一維數組類似,只不過元素是字符。
但是也有很多需要注意的地方
例如其賦值可以
也可以
char d[5] = "Hi";其輸出格式都是用%s輸出的
接下來我們看下面一段程序
在講之前先來復習一下printf中的%s
1.%s是輸出字符串的,接收的參數需要時字符數組變量名或者“xx”雙引號包括在字符串
2.字符串中例如hello,它所占的字節大小不是5個而是6個實際上是 h e l l o \0,%s只有讀取到\0才會判斷為字符讀取完畢。
這個程序中的c和d都可以正常輸出嗎?
可以看到字符數組c輸出是輸出了hello但是后面很混亂而d就完好輸出,這是為什么呢?
遇到bug先看內存
因為我們上面說到%s遇到\0才會停止對字符的讀取
冷知識:兩隊c等于一個燙例如,cc cc =燙
因此這里有3隊cc個燙,其他的直到00停止讀取。
接下來我們把字符數組c變成c[6]看看會怎么樣
結果是對了,我們再回看內存
可以看到總共有一個字節,最后一個是00,這就證明系統回自動加一個\0再最后面,這個也占一個字節。
總結:在我們定義字符數組時要比定義元素總是要比字符數多1
字符數組輸入
輸入也是%s
#include<stdio.h> int main() {char c[20];char d[20];scanf("%s%s", &c, &d);printf("c=%s,d=%s", c, d); }總結
以上是生活随笔為你收集整理的嘉明的C学习之Day8--数组的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高数下(同济大学版本)期中冲刺式复习
- 下一篇: 讯飞输入法pad版x86_讯飞输入法Pa