笨辦法學C 練習8:大小和陣列

Mathilda91發表於2016-04-12

練習8:大小和陣列

原文:Exercise 8: Sizes And Arrays

譯者:飛龍

在上一個練習中你做了一些算術運算,不過帶有'\0'(空)字元。這對於其它語言來說非常奇怪,因為它們把“字串”和“位元組陣列”看做不同的東西。但是C中的字串就是位元組陣列,並且只有不同的列印函式才知道它們的不同。

在我真正解釋其重要性之前,我先要介紹一些概念:sizeof和陣列。下面是我們將要討論的一段程式碼:

#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;
}

這段程式碼中我們建立了一些不同資料型別的陣列。由於陣列是C語言工作機制的核心,有大量的方法可以用來建立陣列。我們暫且使用type name[] = {initializer};語法,之後我們會深入研究。這個語法的意思是,“我想要那個型別的陣列並且初始化為{..}”。C語言看到它時,會做這些事情:

  • 檢視它的型別,以第一個陣列為例,它是int

  • 檢視[],看到了沒有提供長度。

  • 檢視初始化表示式{10, 12, 13, 14, 20},並且瞭解你想在陣列中存放這5個整數。

  • 在電腦中開闢出一塊空間,可以依次存放這5個整數。

  • 將陣列命名為areas,也就是你想要的名字,並且在當前位置給元素賦值。

areas的例子中,我們建立了一個含有5個整數的陣列來存放那些數字。當它看到char name[] = "Zed";時,它會執行相同的步驟。我們先假設它建立了一個含有3個字元的陣列,並且把字元賦值給name。我們建立的最後一個陣列是full_name,但是我們用了一個比較麻煩的語法,每次用一個字元將其拼寫出來。對C來說,namefull_name的方法都可以建立字元陣列。

在檔案的剩餘部分,我們使用了sizeof關鍵字來問C語言這些東西佔多少個位元組。C語言無非是記憶體塊的大小和地址以及在上面執行的操作。它向你提供了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"
$

現在你可以看到這些不同printf呼叫的輸出,並且瞥見C語言是如何工作的。你的輸出實際上可能會跟我的完全不同,因為你電腦上的整數大小可能會不一樣。下面我會過一遍我的輸出:

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

  5

  我的電腦認為int的大小是4個位元組。你的電腦上根據位數不同可能會使用不同的大小。

  6

  areas中含有5個整數,所以我的電腦自然就需要20個位元組來儲存它。

  7

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

  8

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

  9~11

  我們對name陣列執行同樣的操作,但是注意到陣列的大小有些奇怪,它佔4個位元組,但是我們用了三個字元來打出"Zed"。那麼第四個字元是哪兒來的呢?

  12~13

  我們對full_name陣列執行了相同的操作,但它是正常的。

  13

  最後我們列印出namefull_name,根據printf證明它們實際上就是“字串”。

確保你理解了上面這些東西,並且知道這些輸出對應哪些建立的變數。後面我們會在它的基礎上探索更多關於陣列和儲存空間的事情。

如何使它崩潰

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

  • full_name最後的'\0'去掉,並重新執行它,在valgrind下再執行一遍。現在將full_name的定義從main函式中移到它的上面,嘗試在Valgrind下執行它來看看是否能得到一些新的錯誤。有些情況下,你會足夠幸運,不會得到任何錯誤。

  • areas[0]改為areas[10]並列印,來看看Valgrind會輸出什麼。

  • 嘗試上述操作的不同變式,也對namefull_name執行一遍。

附加題

  • 嘗試使用areas[0] = 100;以及相似的操作對areas的元素賦值。

  • 嘗試對namefull_name的元素賦值。

  • 嘗試將areas的一個元素賦值為name中的字元。

  • 上網搜尋在不同的CPU上整數所佔的不同大小。

相關文章