1. 有時候題目沒説要讀入,但是裏面變量都沒有初始化初值,這時候可能是題目沒説清楚。

課後作業8 5-1

計算字符串的有效長度。字符串的有效長度就是有效字符的個數,即數組中第1個 '\0' 前面的字符個數。例如,字符串"Happy"的有效長度是5。

#include <stdio.h>

int main()
{
    int k, len;
    char str[81];

    k = 0;
    while (...) {
        k++;
    }
    ...
    len = ...;

    printf("%d\n", len);

    return 0;
}

題面並沒有説要讀入,但是感覺這裏確實是要讀入,怪題。

2. 課本的運算符優先級和結合律不全,看這張:

Go語言愛好者週刊:第 125 期— 已持續 2 年半了_賦值


int a = 3, *p = &a;
*p++; // 和 a ++ ; 等價

這看似是對的,實際上後綴 ++ 的優先級比間接訪問運算符 * 要高,所以是先取 *p,然後再 p ++ 。和 a ++ 不等價。

由於這倆優先級就不一樣了,所以和結合方向沒有關係。(課本把前綴和後綴自加/自減混為一談了)

3. 理論題注意輸入的結尾標誌,如果沒有説明,就是EOF,但絕對不是 '\0'

課後作業8 1-3

以下代碼實現從鍵盤輸入一個字符串,並將小寫字母轉換為相應的大寫字母后輸出,其他字符原樣輸出。

while ((ch = getchar()) != '\0') {
    if (ch >= 'a' && ch <= 'z')
        ch = ch - 'a' + 'A';
    putchar(ch);
}

這是錯的,'\0' 應該改成 EOF

4. 不知道指針可不可以直接賦值一個整型常量作為地址。

課後作業8 1-18

執行語句int *p = 1000;後,指針變量p指向地址為1000的變量。

dev實測是可以這樣乾的:

int main()
{
	int b = 1;
	int c = (int)(&b);
	int *d = c;
    printf("%d\n", (int)d);
    *d = 2;
    printf("%d\n", b);
    return 0;
}

不知道答案是啥,記得看一下。

5. 字符串初始化如果太多,好像會越界。

char s[6] = "Happy\0";

應該會越界?回去看一下答案。

6. 字符串常量存儲在只讀數據段,這部分內存在程序運行時是隻讀的。

char *p = "hello"; 
*p = 'H';

將導致段錯誤,因為修改了只讀內存。

7. 不同類型的指針變量不可以直接相互賦值。

不同類型指針變量賦值需要強制轉換,但是 void 是例外。

void 不能直接 *p 取值,要強制轉換成別的類型,然後才能用間接運算符。

試圖直接讀取空指針的內容,編譯會報Error。

要這樣寫*(int*)p,也就是先轉成具體類型的變量,這樣編譯器才能得知偏移量等信息。

int main() {
    int a = 10;
    double b = 3.14;
    void *void_ptr;      // void* 可以指向任何類型
    
    void_ptr = &a;       // ✅ int* 可以賦值給 void*
    void_ptr = &b;       // ✅ double* 可以賦值給 void*
    
    int *int_ptr = void_ptr;     // ❌ 需要強制轉換(C++ 中錯誤,C 中警告)
    int_ptr = (int*)void_ptr;    // ✅ 需要顯式轉換
    
    return 0;
}

查出答案,這個PTA題目本身,是説不能直接賦值,但是dev-C不會報錯,看看答案。

課後作業8 1-12

8. 指針可以判斷相等,賦值,相減(得到偏移量),但是不能相加!

相減得到ptrdiff_t有符號整數,大小看操作系統。

9. 字符數組初始化的神秘小trick

char s[100]={"hello",};

多一個逗號不會報錯,但是隻能有一個字符串,如果寫了倆會報錯。

char s[100]={"hello"," world"};

[Error] excess elements in struct initializer

10. 發現一個二分檢查死循環的好方法

  • mid = (l + r) / 2mid 可能等於 l
  • mid = (l + r + 1) / 2mid 可能等於 r

可以這樣考慮,如果 lr 相差得很遠,就沒有問題,一直到 l 等於 r 或者 r-1 的時候,特別考慮即可。

實際上,r - l >= 2 的時候,mid 不會等於 lr 中的任何一個。

11. 不要漏讀題意……

課後作業8 5-14

是負數嗎?

輸入一個以#結束的字符串,濾去所有的非十二進制字符(不分大小寫),組成一個新的表示十二進制數字的字符串,並輸出新的字符串。過濾時,如果在第一個十二進制數字字符前出現-,代表該字符串對應的數是負數,則先輸出-,再輸出字符串。題目假設過濾後的字符串不為空。

#include <stdio.h>
int main()
{
    int flag, i, j;
    char str[81], newstr[81];

    i = 0;
    while ((str[i]=getchar()) != EOF) {
        i++;
    }
    str[i] = '\0';

    i = j = 0;
    flag = 0;
    while (str[i] != '\0') {
        if ((str[i] >= '0' && str[i] <= '9') || (str[i] == 'A' || str[i] == 'B')) {
            newstr[j] = str[i];
            j++;
        }
		else if (j == 0 && str[i] == '-') {
            flag = 1;
        }
        i++;
    }
    newstr[j] = '\0';

    if (flag == 1) {
        putchar('-');
    }
    for(i = 0; newstr[i] != '\0'; i++) {
        putchar(newstr[i]);
    }
    putchar('\n');
    
    return 0;
}
  • 結束表示 #,不是EOF或者別的。
  • 不區分大小寫,ab 也是合法的……

可以嘗試構造hack樣例,然後就會注意到題意。

12. 看清楚程序填空題for後面給沒給括號……

13. 三次賦值交換變量值,不要寫錯。

temp=a[k],a[k]=a[index],a[index]=temp;

我服了,最後一個temp一開始寫成a[k]了……

14. 注意數組名稱是什麼,不要被思維慣性帶跑,下意識一維是 anum 之類。

15. 字符數組初始化,如果有給定長度,下面五種寫法等價。

static char s[6] = {'H', 'a', 'p', 'p', 'y', '\0'};
static char s[6] = {'H', 'a', 'p', 'p', 'y'};
static char s[6] ="Happy";
static char s[6] ={"Happy"};
static char s[6] = {'H', 'a', 'p', 'p', 'y', 0};

要是沒有寫數組大小,第二種 static char s[6] = {'H', 'a', 'p', 'p', 'y'}; 就是錯的了!