鍍金池/ 教程/ C/ 練習8:大小和數(shù)組
練習9:數(shù)組和字符串
練習6:變量類型
練習3:格式化輸出
練習4:Valgrind 介紹
練習28:Makefile 進階
練習14:編寫并使用函數(shù)
練習21:高級數(shù)據(jù)類型和控制結(jié)構(gòu)
練習20:Zed的強大的調(diào)試宏
練習18:函數(shù)指針
練習0:準備
練習15:指針,可怕的指針
練習27:創(chuàng)造性和防御性編程
練習22:棧、作用域和全局
練習10:字符串數(shù)組和循環(huán)
練習8:大小和數(shù)組
練習16:結(jié)構(gòu)體和指向它們的指針
練習7:更多變量和一些算術(shù)
練習23:認識達夫設(shè)備
練習12:If,Else If,Else
練習2:用Make來代替Python
練習1:啟用編譯器
練習11:While循環(huán)和布爾表達式
練習5:一個C程序的結(jié)構(gòu)
練習24:輸入輸出和文件
練習25:變參函數(shù)
練習13:Switch語句
練習19:一個簡單的對象系統(tǒng)
練習26:編寫第一個真正的程序
導言:C的笛卡爾之夢
練習17:堆和棧的內(nèi)存分配

練習8:大小和數(shù)組

在上一個練習中你做了一些算術(shù)運算,不過帶有'\0'(空)字符。這對于其它語言來說非常奇怪,因為它們把“字符串”和“字節(jié)數(shù)組”看做不同的東西。但是C中的字符串就是字節(jié)數(shù)組,并且只有不同的打印函數(shù)才知道它們的不同。

在我真正解釋其重要性之前,我先要介紹一些概念:sizeof和數(shù)組。下面是我們將要討論的一段代碼:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int areas[] = {10, 12, 13, 14, 20};
    char name[] = "Zed";
    char full_name[] = {
        'Z', 'e', 'd',
         ' ', 'A', '.', ' ',
         'S', 'h', 'a', 'w', '\0'
    };

    // WARNING: On some systems you may have to change the
    // %ld in this code to a %u since it will use unsigned ints
    printf("The size of an int: %ld\n", sizeof(int));
    printf("The size of areas (int[]): %ld\n",
            sizeof(areas));
    printf("The number of ints in areas: %ld\n",
            sizeof(areas) / sizeof(int));
    printf("The first area is %d, the 2nd %d.\n",
            areas[0], areas[1]);

    printf("The size of a char: %ld\n", sizeof(char));
    printf("The size of name (char[]): %ld\n",
            sizeof(name));
    printf("The number of chars: %ld\n",
            sizeof(name) / sizeof(char));

    printf("The size of full_name (char[]): %ld\n",
            sizeof(full_name));
    printf("The number of chars: %ld\n",
            sizeof(full_name) / sizeof(char));

    printf("name=\"%s\" and full_name=\"%s\"\n",
            name, full_name);

    return 0;
}

這段代碼中我們創(chuàng)建了一些不同數(shù)據(jù)類型的數(shù)組。由于數(shù)組是C語言工作機制的核心,有大量的方法可以用來創(chuàng)建數(shù)組。我們暫且使用type name[] = {initializer};語法,之后我們會深入研究。這個語法的意思是,“我想要那個類型的數(shù)組并且初始化為{..}”。C語言看到它時,會做這些事情:

  • 查看它的類型,以第一個數(shù)組為例,它是int。
  • 查看[],看到了沒有提供長度。
  • 查看初始化表達式{10, 12, 13, 14, 20},并且了解你想在數(shù)組中存放這5個整數(shù)。
  • 在電腦中開辟出一塊空間,可以依次存放這5個整數(shù)。
  • 將數(shù)組命名為areas,也就是你想要的名字,并且在當前位置給元素賦值。

areas的例子中,我們創(chuàng)建了一個含有5個整數(shù)的數(shù)組來存放那些數(shù)字。當它看到char name[] = "Zed";時,它會執(zhí)行相同的步驟。我們先假設(shè)它創(chuàng)建了一個含有3個字符的數(shù)組,并且把字符賦值給name。我們創(chuàng)建的最后一個數(shù)組是full_name,但是我們用了一個比較麻煩的語法,每次用一個字符將其拼寫出來。對C來說,namefull_name的方法都可以創(chuàng)建字符數(shù)組。

在文件的剩余部分,我們使用了sizeof關(guān)鍵字來問C語言這些東西占多少個字節(jié)。C語言無非是內(nèi)存塊的大小和地址以及在上面執(zhí)行的操作。它向你提供了sizeof便于你理解它們,所以你在使用一個東西之前可以先詢問它占多少空間。

這是比較麻煩的地方,所以我們先運行它,之后再解釋。

你會看到什么

$ make ex8
cc -Wall -g    ex8.c   -o ex8
$ ./ex8
The size of an int: 4
The size of areas (int[]): 20
The number of ints in areas: 5
The first area is 10, the 2nd 12.
The size of a char: 1
The size of name (char[]): 4
The number of chars: 4
The size of full_name (char[]): 12
The number of chars: 12
name="Zed" and full_name="Zed A. Shaw"
$

現(xiàn)在你可以看到這些不同printf調(diào)用的輸出,并且瞥見C語言是如何工作的。你的輸出實際上可能會跟我的完全不同,因為你電腦上的整數(shù)大小可能會不一樣。下面我會過一遍我的輸出:

譯者注:16位機器上的int是16位的,不過現(xiàn)在16位機很少見了吧。

  5

  我的電腦認為int的大小是4個字節(jié)。你的電腦上根據(jù)位數(shù)不同可能會使用不同的大小。

  6

  areas中含有5個整數(shù),所以我的電腦自然就需要20個字節(jié)來儲存它。

  7

  如果我們把areas的大小與int的大小相除,我們就會得到元素數(shù)量為5。這也符合我們在初始化語句中所寫的東西。

  8

  接著我們訪問了數(shù)組,讀出areas[0]areas[1],這也意味著C語言的數(shù)組下標是0開頭的,像Python和Ruby一樣。

  9~11

  我們對name數(shù)組執(zhí)行同樣的操作,但是注意到數(shù)組的大小有些奇怪,它占4個字節(jié),但是我們用了三個字符來打出"Zed"。那么第四個字符是哪兒來的呢?

  12~13

  我們對full_name數(shù)組執(zhí)行了相同的操作,但它是正常的。

  13

  最后我們打印出namefull_name,根據(jù)printf證明它們實際上就是“字符串”。

確保你理解了上面這些東西,并且知道這些輸出對應哪些創(chuàng)建的變量。后面我們會在它的基礎(chǔ)上探索更多關(guān)于數(shù)組和存儲空間的事情。

如何使它崩潰

使這個程序崩潰非常容易,只需要嘗試下面這些事情:

  • full_name最后的'\0'去掉,并重新運行它,在valgrind下再運行一遍?,F(xiàn)在將full_name的定義從main函數(shù)中移到它的上面,嘗試在Valgrind下運行它來看看是否能得到一些新的錯誤。有些情況下,你會足夠幸運,不會得到任何錯誤。
  • areas[0]改為areas[10]并打印,來看看Valgrind會輸出什么。
  • 嘗試上述操作的不同變式,也對namefull_name執(zhí)行一遍。

附加題

  • 嘗試使用areas[0] = 100;以及相似的操作對areas的元素賦值。
  • 嘗試對namefull_name的元素賦值。
  • 嘗試將areas的一個元素賦值為name中的字符。
  • 上網(wǎng)搜索在不同的CPU上整數(shù)所占的不同大小。