首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C/C++ 易錯知識點總結:sizeof/strlen、陣列退化、大數組分配、二進制 IO、遞歸棧溢出

     編輯:關於C

1. sizeof vs strlen —— 經典混淆點

核心區別

sizeofstrlen
性質編譯期運算符運行期函數
返回值類型/對象佔用**位元組數**字串**字符數** (不含 \0)
陣列參數**不退化**,得整個陣列大小退化為指針,得字串長度
指針參數得指針大小 (4/8)得字串長度

實戰陷阱代碼

char *src = "hello world";
char *dest = NULL;
int len = strlen(src);        // 正確:11
// int len = sizeof(src);     // 錯誤:得 4/8 (指針大小)
dest = (char*)malloc(len + 1); // +1 留給 \0
// dest = malloc(len);         // 錯誤:漏掉結束符空間
char *d = dest;
char *s = &src[len - 1];      // 指向最後一個字符
while (len-- != 0) *d++ = *s--; // 反向複製
*d = '\0';                    // 必須手動添加結束符
printf("%s\n", dest);         // "dlrow olleh"
free(dest);                   // 必須配對釋放

2. 陣列與指針的易錯場景

陣列名 & 取地址


int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a + 1);   // &a 是 int(*)[5],+1 跳過整個陣列
printf("%d, %d\n", *(a+1), *(ptr-1)); // 2, 5
  • &a 類型為「指向 5 個 int 陣列的指針」,步長 = sizeof(a) = 20 字節。
  • ptr - 1 回退到陣列最後一個元素 a[4]。

二維陣列/指針參數不匹配


void show(const char **pp) { printf("%s\n", *pp); }

char array[4] = "abc";  // char[4]
char *pointer = "abc";  // char*

show(&pointer); // OK:&pointer 是 char**
show(&array);   // 錯誤:&array 是 char(*)[4],非 char** → 輸出亂碼

**結論**:陣列名在**表達式**中多數退化為指針,但 sizeof、&、_Alignof 等運算符**不退化**。

3. 大陣列分配:棧 vs 堆

  • int a[1000000]; → **棧溢出**(默認棧 1~8 MB,int 4 bytes → 4 MB 超限)。
  • int *p = malloc(1000000 * sizeof(int)); → **堆分配**,僅受限於虛擬記憶體/碎片。
  • 靜態/全局陣列:static int a[250000]; 放在 .bss 段,不佔棧空間。

4. 文件 IO:文本 vs 二進制

文本模式 (fprintf/fscanf)

  • 適合人類可讀、跨平台交換。
  • fprintf(fp, "%d", a[i]); 寫入 ASCII 碼。

二進制模式 (fwrite/fread) —— 必須用


FILE *fp = fopen("out.dat", "wb");  // "wb" 二進制寫
fwrite(&a, sizeof(int), 1, fp);     // 寫入 4 字節原始位模式
fclose(fp);

fp = fopen("out.dat", "rb");        // "rb" 二進制讀
fread(&b, sizeof(int), N, fp);      // 直接讀回 int
fclose(fp);

⚠️ **切勿**用 fprintf(fp, "%d", ...) 寫二進制文件,會產生可讀文本而非原始位模式。

5. 遞歸棧溢出:快速排序實測

現象

  • 快速排序 (遞歸) 在 20 萬元素時棧溢出 (棧默認 2~8 MB)。
  • 最壞情況遞歸深度 O(n),棧幀累積導致溢出。

對比數據 (作者實測)

算法18 萬元素耗時最大安全元素數
快速排序 (首中尾取中優化)0.497 s~3 萬
VC6 qsort()1.942 s較大
堆排序0.032 s無棧限制
希爾排序 (優化版)8.518 s無棧限制

遞歸實現原理

每次遞歸調用在棧上壓入:返回地址、參數、局部變量、保存的寄存器。
Fac(3) → 3*Fac(2) 入棧 → 2*Fac(1) 入棧 → 返回展開。
**棧幀大小 × 遞歸深度 = 總棧消耗**。

避坑建議

  1. **大數據量排序優先用非遞歸算法**:堆排序、歸併排序 (迭代版)、std::sort (內部混合非遞歸)。
  2. 必須遞歸時:
    • 尾遞歸優化 (編譯器支援時)。
    • 顯式模擬棧 (手動 std::vector/數組做棧)。
    • 增加棧大小 (鏈接器選項 /STACK、pthread_attr_setstacksize)。
  3. 嵌入式/實時系統:**嚴禁無界遞歸**。

6. 綜合最佳實踐清單

  • 字串長度用 strlen,對象大小用 sizeof,絕不混用。
  • malloc 大小用 sizeof(Type) * N,記得 +1 給字串結束符。
  • 二進制文件只用 fread/fwrite + "rb"/"wb"。
  • 陣列做函數參數**退化為指針**,sizeof 失效,需額外傳長度。
  • 遞歸深度不可控 → 改迭代或顯式棧。
  • 編譯器警告全開 (-Wall -Wextra -pedantic / /W4),靜態分析工具 (Cppcheck, Clang Static Analyzer) 定期掃描。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved