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

C語言編程經驗積累(四):從 sizeof 陷阱到結構體對齊的 30 個實戰筆記

     編輯:關於C語言

前言

記錄 C 語言開發中易混淆、易出錯的知識點,涵蓋運算符優先級、記憶體佈局、指標運算、編譯鏈接等核心領域,作為查漏補缺參考。

核心筆記精選

1. 三元運算符賦值

x = y > z ? y : z; // 正確:條件 ? 真值 : 假值
// 切記括號:x = (y > z) ? y : z; 避免優先級歧義

2. 運算符優先級陷阱

  • + - > 移位 << >> > 關係 < > > 位與 & > 位異或 ^ > 位或 | > 邏輯與 && > 邏輯或 || > 三元 ?: > 賦值 =
  • 典型錯誤:a << 8 + b 實際為 a << (8 + b),應寫 (a << 8) + b

3. sizeof 編譯期計算特性

int i = 0;
sizeof(i++); // 返回 4 (int 大小),i 不會自增!
// sizeof 不計算表達式值,僅根據類型推導大小
// strlen 運行期計算,遇 \0 停止

4. 強制轉換優先級高於加減

// 錯誤示範
#define Uint8 unsigned char
typedef Uint8 MyUint8; // 展開為 typedef unsigned char MyUint8; 正確
// 但若
#define PINT int*
typedef PINT MyPtr; // 展開為 typedef int* MyPtr; 正確
// 危險:#define 文本替換,typedef 語義定義,勿混用

6. printf 輸出 % 號

printf("%%\n"); // 雙重 %% 轉義輸出單個 %

7. 邏輯運算 vs 位運算

if (a && b) // 邏輯與:短路,結果 0/1
if (a & b)  // 位與:逐位運算,結果數值

8. 取反優先級

!a + b  等價於  (!a) + b  // 邏輯非優先級最高
~a + b  等價於  (~a) + b  // 位非優先級同單目運算符

9. strcpy vs strlen

  • strcpy 複製含 \0 的完整字串
  • strlen 統計 \0 前字符數,不含 \0

10. 位操作宏

// 第 5 位置 1
A |= (1 << 5);
// 第 5 位清 0
A &= ~(1 << 5);
// 第 5 位取反
A ^= (1 << 5);

11. union 賦值語法

union Data { int i; float f; } u;
u.i = 10; // 必須用 . 成員訪問

12. 16進制格式化輸出

printf("0x%08x\n", val); // 8位寬度,前導補 0

13. 棧幀結構

函數調用棧幀包含:參數、返回地址 (EIP)、舊幀基址 (EBP)、局部變數、暫存寄存器。x86 向下增長(高地址→低地址)。

14. 結構體記憶體對齊實例

struct Test { char x1; short x2; float x3; char x4; };
// x1: offset 0, size 1
// padding 1 byte (對齊 short 2字節)
// x2: offset 2, size 2
// x3: offset 4, size 4 (自然對齊)
// x4: offset 8, size 1
// padding 3 bytes (結構體整體對齊到最大成員 4 字節)
// 總大小 = 12 字節

15. memset vs memcpy

memset(ptr, 0, 100);     // 將 100 字節置 0
memcpy(dst, src, n);     // 複製 n 字節,記憶體不可重疊
memmove(dst, src, n);    // 安全複製,允許重疊

16. 多進程共享動態庫全局變量

每進程獨立地址空間,數據段複製,全局變量互不干擾,無競爭。

17. 結構體陣列靜態初始化

static struct { char *name; int offset; } _reg_list[] = {
    {"reg1", 0x10}, {"reg2", 0x20}
};

18. for 循環 break 行為

for (i=0; i<10; i++) { if (cond) break; }
// break 後 i 不再自增,保持當前值

19. 循環計數器模 8

temp = (temp + 1) & 7; // 0→1→...→7→0,位運算替代 %8

20. 指針賦值類型匹配

unsigned char *p = arr;      // 正確:指針賦值給同類型指針
unsigned int p1 = (unsigned int)arr; // 需強轉:指針轉整數
unsigned char *p_arr[10];    // 指針陣列:10個 char*
unsigned char (*p_arr2)[10]; // 陣列指針:指向 10 char 的陣列

21. if-else if 鏈 vs 多個 if

  • if-else if:互斥,命中一個後跳過其餘判斷
  • 多個 if:獨立判斷,可能多個分支均執行

22. 二維數組名類型

char a[3][4];
char (*p)[4] = a; // 正確:行指針,指向 4 char 的陣列
char **p2 = a;    // 錯誤:類型不匹配,a 不是 char**

23. 數組名 &a vs a

  • a:類型 char (*)[4],值為首元素地址 &a[0][0]
  • &a:類型 char (*)[3][4],值同樣是首地址,但 &a+1 偏移整個陣列大小 (12*sizeof(char))

24. 文件大小獲取

fseek(fp, 0, SEEK_END);
long size = ftell(fp);
fseek(fp, 0, SEEK_SET); // 復位

25. 無第三變量交換

a ^= b; b ^= a; a ^= b; // 異或交換
a += b; b = a - b; a -= b; // 加減交換(注意溢出)

總結

這 30 個筆記覆蓋了 C 語言從語法細節到底層實現的關鍵點。建議開發者:

  1. 遇到不確定行為時查閱標準文檔(C99/C11)
  2. 善用編譯器警告 -Wall -Wextra -pedantic
  3. 關鍵模塊編寫單元測試驗證邊界條件
  4. 定期複習《C專家編程》《C陷阱與缺陷》等經典著作
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved