C 語言學習 第12次做業總結

做業總結

本次課堂的內容爲字符串相關的幾個函數還有結構體。程序員

字符串相關函數

在此以前的課程中,輸入主要都是使用scanf這個函數。而在這節課上,馮老師講解了字符串獲取函數gets。在不須要控制符的狀況下,gets函數均可以取代scanf數組

  • strcpy函數:實現一個字符串到另外一個字符串的拷貝
  • strcat函數:實現兩個字符串鏈接爲一個新的字符串
  • strcmp函數:實現兩個字符串大小的比較(基於 ASCII 碼)
  • strlen函數:得到一個字符串的長度數據(須要知道的是,字符串的實際長度還須要加一個尾綴\0

結構體

馮老師在課堂上講了一些結構體的知識,可是結構體中有一些細節的東西沒有涉及,我在這裏稍做整理,部分同窗後期的課程會遇到(主要針對學習目標爲嵌入式的同窗)。函數

位域的概念

以前在課堂上,馮老師會告訴你們,結構體的內容是基本數據類型或者是結構數據類型,這裏其實還有一種類型就是位域化的基本數據類型:學習

struct {
  int a;
  int b;
}

先考察上面的這個結構體的內容,它包含有兩個基本數據類型的變量ab。在內存中,他們將會佔用2 * sizeof(int)個內存空間。在某些狀況下,可能僅想要用一位來表示一個數據是否被使用(即真或者假),那麼對於上面的結構體,空間就浪費的過於厲害。這時能夠修改以上的結構體:設計

struct {
  int a :1;
  int b :1;
}

這時候,這個結構體的內存佔用就變成了一個sizeof(int),雖然仍是2int類型的變量,可是每個變量都僅佔用一個位的內存。指針

對齊和補齊

雖然結構體和數組都是一種「固實」的數據類型(表如今內存中爲連續存儲)。可是稍有不一樣是的,數組的確是一種真的「固實」數據——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類型都歸併到一塊兒。

C99 中的結構體初始化的方式

同窗們如今學習的 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)。如今,將內存想象爲一個可樂的瓶子,堆內存就是堆在瓶底的內存,它會一點點的向上生長;棧內存就是在瓶頂的內存,它會一點點的向下生長。

  • 堆內存(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
相關文章
相關標籤/搜索