本次課堂的內容爲字符串相關的幾個函數還有結構體。程序員
在此以前的課程中,輸入主要都是使用scanf
這個函數。而在這節課上,馮老師講解了字符串獲取函數gets
。在不須要控制符的狀況下,gets
函數均可以取代scanf
。數組
strcpy
函數:實現一個字符串到另外一個字符串的拷貝strcat
函數:實現兩個字符串鏈接爲一個新的字符串strcmp
函數:實現兩個字符串大小的比較(基於 ASCII 碼)strlen
函數:得到一個字符串的長度數據(須要知道的是,字符串的實際長度還須要加一個尾綴\0
)馮老師在課堂上講了一些結構體的知識,可是結構體中有一些細節的東西沒有涉及,我在這裏稍做整理,部分同窗後期的課程會遇到(主要針對學習目標爲嵌入式的同窗)。函數
以前在課堂上,馮老師會告訴你們,結構體的內容是基本數據類型或者是結構數據類型,這裏其實還有一種類型就是位域化的基本數據類型:學習
struct { int a; int b; }
先考察上面的這個結構體的內容,它包含有兩個基本數據類型的變量a
和b
。在內存中,他們將會佔用2 * sizeof(int)
個內存空間。在某些狀況下,可能僅想要用一位來表示一個數據是否被使用(即真或者假),那麼對於上面的結構體,空間就浪費的過於厲害。這時能夠修改以上的結構體:設計
struct { int a :1; int b :1; }
這時候,這個結構體的內存佔用就變成了一個sizeof(int)
,雖然仍是2
個int
類型的變量,可是每個變量都僅佔用一個位的內存。指針
雖然結構體和數組都是一種「固實」的數據類型(表如今內存中爲連續存儲)。可是稍有不一樣是的,數組的確是一種真的「固實」數據——arr[1]
和arr[2]
之間插不進去一個任意的數據類型。可是結構體就不一樣了。code
這裏先看一個例子:對象
struct { int a; // 假設 int 是 32 位的數據 char b; // 假設 char 是 8 位的數據 int c; }
對這個結構體進行sizeof
操做就會發現結果是12。這時你會好奇的發現,兩個 int
加一個 char
不該該是 9 嗎,這裏爲何變成 12 了呢。其實在結構體中,有這樣的一個要求:結構體內部的數據,老是按照最大的那個數據類型進行對齊(可是當最大的數據類型已經超過 4 個字節,則始終按照 4 個字節對齊)。這裏,能夠看到數據類型最大的是 int
,它佔用 4 個字節,那麼第二個類型 char
只能按照它進行對齊(見下圖,每個方塊都是一個 sizeof(char)
)。這裏,第二行存在 3 個位置的空洞,這些空洞是理論上能夠存儲數據,可是被浪費掉的blog
也許你會好奇這有什麼用處呢。考慮如下的結構體:內存
struct{ int a; char b; int c; char d; int e; char f; int g; char h; }
在對以上的知識有了解後,你必定就能夠知道這樣的結構體的設計是不合理的,最好是將幾個char
類型都歸併到一塊兒。
同窗們如今學習的 C 語言語法大多數都是按照 C89 標準來的,C99 中稍微增長了一點其餘的東西,和結構體相關的有如下這個方法:
在 C89 標準中,若是想要對結構體中的成員變量進行初始化,你可能會那麼作:
struct ABCD abcd; abcd.a = 10; abcd.b = 20; abcd.c = 30; abcd.d = 40;
而在 C99 標準中,提供了一種新的初始化方法:
struct ABCD abcd = { .a = 10; .b = 20; .c = 30; .d = 40; }
經過查看同窗們這次做業的內容,主要發現如下的 3 個問題。
雖然同窗們在以前的總結中都一再的強調說指針變量須要先明確的指向一個對象,而後才能使用,可是在實際的代碼中,的確是屢次發現有同窗沒有初始化指針變量就使用它(或者是指針變量賦值爲 null 後使用它)。對於指向 null 的指針變量,雖然使用通常不會出錯,可是仍是不建議使用,畢竟它自己沒有什麼意義。何況假如將多個功能不一樣的指針,都指向同一個地址,稍微用幾回,也都會出現問題。
這個錯誤很典型:在同窗們的成績管理系統中,添加新成員的時候,不少同窗首先在函數中定義一個結構體對象,而後對對象中的成員變量賦值,在最後將這個結構體對象的地址直接賦值給形參。
出現這個錯誤,不怪你們,由於你們都尚未學習過計算機原理相關的知識,不知道在內存中有堆內存和棧內存的概念。這裏先跟你們簡單的介紹下:
在計算機系統中,程序使用的內存,分爲不少種,其中和變量相關的通常是如下兩種:堆內存(heap)和棧內存(stack)。如今,將內存想象爲一個可樂的瓶子,堆內存就是堆在瓶底的內存,它會一點點的向上生長;棧內存就是在瓶頂的內存,它會一點點的向下生長。
在腦海中存在這樣的兩個簡單的概念後,咱們返回到程序代碼中去。這裏先理解這個概念「程序員分配和釋放」,它是什麼意思呢。通常來講在不特殊指明的狀況下,咱們均可以將程序員分配假想爲函數malloc
或者是 C++
中過的操做符 new
。而將釋放假想爲free
函數。到這裏,你們就能夠發現了,至今本身都尚未學習過與此相關的函數,因此到目前爲止全部的變量都是分配在棧上的。棧上的內存有什麼特色呢——它是由編譯器自動分配和釋放。
先將這個概念稍微放置下,回憶我以前總結中說起的子函數的特色:
在 C 語言中,一切的傳參都是值傳遞。
假想存在一片廣袤的土地。子函數就是在須要調用它是時候,在這片土地上圈了一塊地,在其中挖坑也罷,種樹也好,可是一旦這個函數結束,將會有一輛看不見的推土機轟隆而來,將這圈起來的土地從新歸於廣袤。
以上,就是子函數的特色。如今,開始看代碼:
#include <stdio.h> void return_func(int *arr){ int a = 10; arr = &a; } int main(){ int aa = 0; return_func(&aa); printf("%d\n",aa); return 0; }
在這個代碼中存在一個名爲return_func
的函數,這個 函數的目標就是將傳入的變量的值改成 10。這個函數,和同窗們此次做業要作的內容極其類似,只是我這裏將它簡化成上面的簡單版本。
按照同窗們的理解,這裏最終應該可以實現輸出 10 這一結果。可是實際上,最終的輸出依舊是 0。爲何函數不能生效呢?!如今,用上面形容子函數的方法來看這個題目。
首先,請同窗們不要將我概括的「在 C 語言中,一切的傳參都是值傳遞」奉爲金圭玉臬。由於到如今都未曾在一本 C 語言書中看到相似的話。
// 從主函數開始看起 int main(){ int aa; // 假設變量 aa 的地址是 0x0000 0001 return_func(&aa); // 開始調用子函數,由於須要傳遞參數 // 且參數爲一個地址,因此經過 & 得到 aa 的地址 0x0000 0001 後傳入函數 // 調用函數後進入子函數體,由於傳入的參數是一個指針,因此先須要構造一個變量存放 aa 的地址 // 因而,子函數至關於變成: // void return_func(){ // int *arr = 0x0000 0001; // int a = 10; // arr = &a; // } printf("%d\n",aa); return 0; }
繼續以上的子函數,子函數已經變形爲上面的樣子了,下面開始學說二:清空學說:
void return_func(){ int *arr = 0x0000 0001; int a = 10; // 假設 a 的地址是 0x0000 0002 arr = &a; // 那麼這裏的 arr 存儲的內容就變成爲 0x0000 0002 }
能夠看到這裏的 arr 是一個局部的變量,在這個函數中,它後來被賦值爲 0x0000 0002。看到這裏應該就明白了吧——明明是想要將地址 0x0000 0001 對應的內容修改成 10,可是最後卻將這個地址指向了 0x0000 0002。最最最關鍵的是,按照清空學說,最後這個變量的地址尚未了。到這裏,錯在哪裏就很顯然了。
本次成績統計說明:本次成績,一旦發現做業抄襲狀況,則代碼部分直接清零(馮老師以前和同窗有約定),抄襲狀況暫時尚未整理完畢,做業的完整總結明天給出。
編號 | 學號 | Cnblogs暱稱 | 代碼 | 總結 | 加權得分 | 備註 |
---|---|---|---|---|---|---|
1 | 160809401 | 付胤 | 0 | 60 | 24 | 代碼沒有按照題目要求編寫,沒有第一題和第二題,第三題沒有實現需求 |
2 | 160809402 | 張博洋 | 0 | 59 | 23.6 | 沒有寫代碼,總結敷衍 |
3 | 160809403 | 董宇豪 | 75 | 59 | 68.6 | 變量名不可以見名知意 |
4 | 160809404 | 朱念齊 | 35 | 59 | 44.6 | 使用指針數組解決問題,代碼2是有問題的代碼,沒法經過編譯 |
5 | 160809405 | 蘆彥儒 | 0 | 80 | 32 | 代碼提交時間:2016-12-15 16:42,同11號 |
- | ------ | ------ | --- | --- | --- | ------------------------------------- |
7 | 160809407 | Leonardo#* | -100 | -100 | -100 | |
8 | 160809408 | iL.linker | 90 | 85 | 88 | |
9 | 160809409 | gdcs16_409 | -100 | -100 | -100 | |
10 | 160809410 | 無聲的夢 | 80 | 85 | 82 | 代碼提交時間:12-12 12:57 |
11 | 160809411 | 劉悅 | 0 | 60 | 24 | 代碼提交時間:2016-12-15 13:59,同10號 |
12 | 160809412 | 張磊 | 85 | 75 | 81 | |
13 | 160809413 | 王洪燁 | 0 | 85 | 34 | 代碼提交時間:2016-12-12 11:47,同26號 |
14 | 160809414 | 紀柏如 | 85 | 80 | 83 | |
15 | 160809415 | 閆墨傑 | 70 | 90 | 78 | 做業3實現不完整 |
16 | 160809416 | 史航 | 60 | 75 | 66 | 做業3沒有寫,做業1編寫正確 |
17 | 160809417 | 狂歡 | -100 | -100 | -100 | |
18 | 160809418 | 水母Jam | -100 | -100 | -100 | |
19 | 160809419 | 朱鈺鋮 | -100 | -100 | -100 | |
20 | 160809420 | 虞小生 | 40 | 70 | 52 | 代碼3沒有寫,代碼1實現錯誤,使用strcpy沒法實現長度判斷 |
21 | 160809421 | 飲冰少年1 | 60 | 60 | 60 | |
---- | ---------- | --------- | --- | --- | --- | ----------------------------------------- |
23 | 160809423 | 李筱 | -100 | -100 | -100 | |
24 | 160809424 | Xzy! | 30 | 65 | 44 | 做業3沒有寫,做業1實現錯誤 |
25 | 160809425 | 剎那神華 | 70 | 70 | 70 | 做業3僅實現增長同窗的函數 |
26 | 160809426 | zlt.Santorini'Ly | 60 | 80 | 68 | 代碼提交時間:2016-12-11 21:28 |
27 | 160809427 | 江超民 | 0 | 90 | 36 | 代碼提交時間:2016-12-14 22:14,同26號 |
28 | 160809428 | zxkai | 90 | -100 | 14 | 沒有總結 |
29 | 160809429 | 王鑫沐 | 70 | 85 | 76 | |
30 | 160809430 | 茉妍 | 65 | 85 | 73 | |
31 | 160809431 | 茉莉雨 | 95 | 85 | 91 |