C陷阱與缺陷(二)_licong0527-ChinaUnix博客

今天有上課了,王老師與我們分享了一些筆試容易出錯的問題,我把它總結出來與大家一起學習。
1. 求下面程序的結果:

點擊(此處)摺疊或打開

    int
    main(void)
    {
    unsigned short A = 10;
    printf("~A = %u\n", ~A);

    char c = 128;
    printf("c = %d\n", c);
    }
    運算結果:4294967285(我的機子上,short是16位的) -128
分析:第一個printf()語句考察了對數據進行按位求反,最後的結果以%u無符號的形式打印出來。按位求反以後,最高位爲有效位,而不是符號位,我們將其轉換爲十進制打印出來。 第二個printf()語句考察了數據類型char默認是有符號(signed)還是無符號的(unsigned)。這個題很容易讓人答錯,運行結果後是-128。從結果可以發現,若是無符號數的話,以十進制打印出來,應該爲128。所以,可以確定char默認爲有符號數(signed)。128對於有符號的char來說,發生了溢出,所以結果爲-128。
2. 求輸出結果:

點擊(此處)摺疊或打開

    void
    GetMemory(char **p, int num)
    {
    *p = (char *)malloc(num);
    }
    int
    main(void)
    {
    char *str = NULL;

    GetMemory(str, 100);
    strcpy(str, "hello");
    free(str);
    if( str != NULL )
    strcpy(str, "world");
    printf("str is %s\n", str);}運行結果:str is world
分析:這個題其實是有bug的。main()調用GetMemory(),GetMemory()調用了malloc(),之後將hello拷貝到str中,此時,str爲hello。接下來,free把malloc()申請的空間釋放掉了。 注意,我們free掉申請的空間後,並沒有將str這個指針的值改變。我們在free(str)前、後分別加上一條語句:printf("%x\n", str);,會發現兩個地址是一樣的,下面是我的電腦運行出來的結果:
我們緊接着又給str拷貝了world,可能很多人會和我有一樣的感覺,就是這是錯誤的,明明將空間釋放掉了,怎麼能拷貝到裏面呢? 在拷貝world之前,有一條if()語句,判斷str是否爲NULL,從上面的打印結果可以看到,str並不爲NULL,所以會進行下面的拷貝語句。不過,特別要注意,打印出str is world,這完全是一個巧合,因爲我們在free與後來的拷貝之間並沒有進行新的空間申請,所以,雖然釋放了空間,但是我們可以找到地址,在這塊內存上進行復制操作。如果我們在free後,又申請了新的空間,剛剛好把這塊空間申請了,可能結果就不是我們想要的了。 我們自己平時使用時,一定要避免這種錯誤。方法是在free()後,將指針置爲NULL,這樣,由於指針爲空,所以對它進行復制操作必然會發生錯誤。
3. 求下面程序的運行結果:

點擊(此處)摺疊或打開

    int
    main(void)
    {
    char a[10];

    printf("%d\n", strlen(a));

    return 0;
    }
分析:這個程序重點考察了數組的初始化和strlen()的用法。我的電腦上運行這個程序的結果爲2,這是一個隨機值,下面我將詳細說明:這是我將上面的程序進行擴充後的代碼:

點擊(此處)摺疊或打開

    int
    main(void)
    {
    char a[10];
    int i;

    for(i = 0; i < 10; i)
    printf("a[%d]:%d\n", a[i]);
    printf("length:%d\n", strlen(a));

    return 0;
    }
下面是運行結果:
從運行結果可以看到,a數組中存入的值爲垃圾值。length()求的是字符串的長度,到'\0'爲止的長度。由此可知,strlen(a)的結果爲2。2是一個隨機值,每個人的機子上運行出來的結果可能都不一樣,因爲每個人機子上,數組初始化幾個元素是不定的。有的人的機子上可能會打印出大於10的值,這是因爲strlen()在求值的時候,從數組第一個元素開始查找,找到連續內存中的第一個'\0'停止,即爲strlen()的結果。 特別強調一下,如果上述strlen()換爲sizeof()的話,結果爲10。strlen()與數組的初始化有關,而sizeof()與初始化無關。
4. x = 9999, 求下面函數的返回值

點擊(此處)摺疊或打開

    int
    func(int x)
    {
    int countx = 0;

    while(x)
    {
    countx ;
    x = x (x-1);
    }

    return countx;
    }運行結果:8
分析:這道題的重點在於我們要明白函數的功能是什麼。 注意到,在while()循環裏有一個位運算符,這就意味着我們要將x化爲二進制表示形式。循環裏,x不斷的減1,運算後,x的值也就隨之改變。我們知道任何數和1,值都不變;而和0,會在0的對應位將那一位的值置0。會發現,其實countx是在統計二進制數中的1的個數。 那麼,9999中有多少個1呢?這裏有一個方便的方法來計算,而不用我們通常除二的方法來求十進制數的二進制值。我們可以將9999拆爲若干個二進制的冪,然後分別計算每一部分的1的個數:9999 = 9 *1024 1*512 1 *256 1*8 1*4 1*2 1*1 = 9 *(2^10) 1*(2^9) 1*(2^8) 1*(2^3) 1*(2^2) 1*(2^1) 1*(2^0)我們知道2的n次冪爲:1個1,後面跟n個0。那麼,9*(2^10)中含有2個一,因爲9爲1001,有兩個1,乘以2的10次方,1的個數沒變。以此類推,可得9999一共有8個1。
5. 求sizeof(A) = ?(32位機)

點擊(此處)摺疊或打開

    struct A
    {
    char t:4;
    char k:4;
    unsigned short i:8;
    unsigned long m;
    };運行結果:8(gcc編譯器,版本爲4.6.3)
分析:這道題考察了字節對齊問題,同時涉及到了位段。特別說明,不同的編譯器版本,運行出來的結果可能不一樣,本人的機子是gcc 4.6.3。 t佔用了4位,k佔用了4位,i佔用了8位,這是一共佔用了2個字節。因爲gcc下是以4字節對齊的,這時,再放入m的四個字節已經放不下,從另一個存儲單元(視不同的編譯器而定)開始存放,實際存放如下圖(上面的數字是我假設的地址,方便大家看):
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章