C/C++ 作為底層語言,擁有許多微妙的行為特性。掌握這些細節不僅能避免潛在 Bug,更能寫出高效、優雅的代碼。以下整理了幾個經典案例與核心知識點。
printf 的參數求值順序為從右向左,這在涉及副作用(如自增)時尤為關鍵。
char s[] = "123", *p = s;
printf("%c%c%c", *p++, *p++, *p++); // 輸出:321
參數從右至左壓棧,故最右邊的 *p++ 先執行,取到 '1',但指針已移至 '2';中間取 '2',指針移至 '3';最左邊取 '3'。最終輸出順序為 3、2、1。
x = x & (x - 1) 每執行一次,便將 x 二進制中最低位的 1 清零。利用此性質可高效統計 1 的個數(Hamming Weight)。
int countOnes(unsigned int n) {
int cnt = (n == 0) ? 0 : 1;
while (n = (n & (n - 1))) cnt++;
return cnt;
}
fork() 執行一次,返回兩次:父進程返回子進程 PID,子進程返回 0。|| 短路:左邊為真則不執行右邊。int main() { fork() || fork(); }
// 產生 3 個進程:主進程 fork() 返回子 PID(非0) -> 短路,不執行第二個 fork()。
// 子進程1 fork() 返回 0 -> 執行第二個 fork() 產生子進程2。
// 子進程2 fork() 返回 0 -> 短路結束。
continue:跳過本次循環剩餘部分,直接進入下一次迭代判斷。break:完全跳出當前循環體。int a, b;
for (a = 1, b = 1; a <= 100; a++) {
if (b >= 20) break;
if (b % 3 == 1) { b += 3; continue; }
b -= 5;
}
printf("%d", a); // 輸出 8
函數遞歸本質是棧操作(先進後出)。以下代碼輸出 "suiene":
void print(char *s) {
if (*s) { print(++s); printf("%c", *s); }
}
int main() { char str[] = "Genius"; print(str); }
遞歸深入至字符串末尾 '\0' 返回,然後依次打印返回地址處的字符,實現逆序輸出(首字符 'G' 不打印,因返回時 *s 已移至下一字符)。
new/malloc 分配,需手動釋放。char *myString() {
char buffer[6] = {0}; // 位於棧
char *s = "Hello World!"; // "Hello World!" 位於靜態區,s 位於棧
// ... 複製內容到 buffer
return buffer; // 錯誤:返回棧地址,函數結束後 buffer 失效
}
int main() {
char *p = "hello,world"; // p 在棧,"hello,world" 在靜態區
}
正確做法:若需返回動態字符串,應使用 new char[] 或 malloc 在堆上分配,或返回 std::string。
深入理解參數求值順序、位運算技巧、進程控制、循環控制、遞歸本質及內存模型,是寫出健壯 C/C++ 程序的基石。實戰中多驗證、多思考底層機制,方能避開坑洼。