在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

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

練習9:數組和字符串

上一個練習中,我們學習了如何創(chuàng)建基本的數組,以及數組怎么樣映射為字符串。這個練習中我們會更加全面地展示數組和字符串的相似之處,并且深入更多內存布局的知識。

這個練習向你展示了C只是簡單地將字符串儲存為字符數組,并且在結尾加上'\0'(空字符)。你可能在上個練習中得到了暗示,因為我們手動這樣做了。下面我會通過將它與數字數組比較,用另一種方法更清楚地實現它。

#include <stdio.h>

int main(int argc, char *argv[])
{
    int numbers[4] = {0};
    char name[4] = {'a'};

    // first, print them out raw
    printf("numbers: %d %d %d %d\n",
            numbers[0], numbers[1],
            numbers[2], numbers[3]);

    printf("name each: %c %c %c %c\n",
            name[0], name[1],
            name[2], name[3]);

    printf("name: %s\n", name);

    // setup the numbers
    numbers[0] = 1;
    numbers[1] = 2;
    numbers[2] = 3;
    numbers[3] = 4;

    // setup the name
    name[0] = 'Z';
    name[1] = 'e';
    name[2] = 'd';
    name[3] = '\0';

    // then print them out initialized
    printf("numbers: %d %d %d %d\n",
            numbers[0], numbers[1],
            numbers[2], numbers[3]);

    printf("name each: %c %c %c %c\n",
            name[0], name[1],
            name[2], name[3]);

    // print the name like a string
    printf("name: %s\n", name);

    // another way to use name
    char *another = "Zed";

    printf("another: %s\n", another);

    printf("another each: %c %c %c %c\n",
            another[0], another[1],
            another[2], another[3]);

    return 0;
}

在這段代碼中,我們創(chuàng)建了一些數組,并對數組元素賦值。在numbers中我們設置了一些數字,然而在names中我們實際上手動構造了一個字符串。

你會看到什么

當你運行這段代碼的時候,你應該首先看到所打印的數組的內容初始化為0值,之后打印初始化后的內容:

$ make ex9
cc -Wall -g    ex9.c   -o ex9
$ ./ex9
numbers: 0 0 0 0
name each: a   
name: a
numbers: 1 2 3 4
name each: Z e d 
name: Zed
another: Zed
another each: Z e d
$

你會注意到這個程序中有一些很有趣的事情:

  • 我并沒有提供全部的4個參數來初始化它。這是C的一個簡寫,如果你只提供了一個元素,剩下的都會為0.
  • numbers的每個元素被打印時,它們都輸出0。
  • names的每個元素被打印時,只有第一個元素'a'顯示了,因為'a'是特殊字符不會顯示。
  • 然后我們首次打印names,打印出了"a",因為它在初始化表達式中的'a'字符之后都用'\0'填充,是以'\0'結尾的正確的字符串。
  • 我們接著通過手動為每個元素賦值的辦法建立數組并且再次把它打印出來??纯此麄儼l(fā)生了什么改變?,F在numbers已經設置好了,看看names字符串是如何正確打印出我的名字的。
  • 創(chuàng)建一個字符串也有兩種語法:第六行的char name[4] = {'a'},或者第44行的char *another = "name"。前者不怎么常用,你應該將后者用于字符串字面值。

注意我使用了相同的語法和代碼風格來和整數數組和字符數組交互,但是printf認為name是個字符串。再次強調,這是因為對C語言來說,字符數組和字符串沒有什么不同。

最后,當你使用字符串字面值時你應該用char *another = "Literal"語法,它會產生相同的東西,但是更加符合語言習慣,也更省事。

如何使它崩潰

C中所有bug的大多數來源都是忘了預留出足夠的空間,或者忘了在字符串末尾加上一個'\0'。事實上,這些bug是非常普遍并且難以改正的,大部分優(yōu)秀的C代碼都不會使用C風格字符串。下一個練習中我們會學到如何徹底避免C風格字符串。

使這個程序崩潰的的關鍵就是拿掉字符串結尾的'\0'。下面是實現它的一些途徑:

  • 刪掉name的初始化表達式。
  • 非故意地設置name[3] = 'A',于是它就沒有終止字符了。
  • 將初始化表達式設置為'a','a','a','a'},于是就有過多的'a'字符,沒有辦法給'\0'留出位置。

試著想出一些其它的辦法讓它崩潰,并且在Valgrind下想通常一樣運行這個程序,你可以看到具體發(fā)生了什么,以及錯誤叫什么名字。有時Valgrind并不能發(fā)現你犯的錯誤,則需要移動聲明這些變量的地方看看是否能找出錯誤。這是C的黑魔法的一部分,有時變量的位置會改變bug。

附加題

  • 將一些字符賦給numbers的元素,之后用printf一次打印一個字符,你會得到什么編譯器警告?
  • names執(zhí)行上述的相反操作,把names當成int數組,并一次打印一個int,Valgrind會提示什么?
  • 有多少種其它的方式可以用來打印它?
  • 如果一個字符數組占四個字節(jié),一個整數也占4個字節(jié),你可以像整數一樣使用整個name嗎?你如何用黑魔法實現它?
  • 拿出一張紙,將每個數組畫成一排方框,之后在紙上畫出代碼中的操作,看看是否正確。
  • name轉換成another的形式,看看代碼是否能正常工作。