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

C語言字串深度解析:記憶體模型、指標陷阱與核心操作

     編輯:關於C語言

C語言字串深度解析:記憶體模型、指標陷阱與核心操作

字串在C語言中並非基本型別,而是以以空字元'\0'結尾的字元陣列形式存在。這一看似簡單的設計,卻孕育了C語言靈活高效也危險的特性。本文從記憶體佈局、建立方式、參數傳遞、修改限制、長度計算五個維度,系統梳理字串核心知識與常見陷阱。

一、 字串 vs 字元:記憶體佈局的本質差異

#include <stdio.h>
int main(void) {
    // "H" 是字串字面量,佔 2 字節:'H' + '\0'
    // 'H' 是字元常數,佔 1 字節(實際為 int 型別,通常 4 字節)
    printf("sizeof(\"c\") = %zu, sizeof('s') = %zu\n", sizeof("c"), sizeof('s'));
    // 輸出: sizeof("c") = 2, sizeof('s') = 4 (64位系統 int 為 4 字節)
    return 0;
}

結論: 雙引號建立的是字串字面量(含結束符),單引號建立的是字元常數(整型值)。

二、 字串字面量的記憶體歸屬與指標綁定

char *p = "hello world!";

這行代碼背後發生了什麼?

  1. 編譯期: 編譯器在唯讀資料段分配 13 字節空間存儲 "hello world!\0"。
  2. 表達式值: "hello world!" 的值是該字串首字元地址(即字元 'h' 的地址)。
  3. 指標初始化: p 指向這個地址。

驗證:

printf("%s\n", p);        // 輸出整個字串
printf("%c\n", *("hello")); // 輸出 'h',字串字面量直接解引用獲取首字元

三、 致命陷阱:嘗試修改字串字面量

char *p = "hello world!\n";
p[0] = 'H'; // 編譯可能通過,運行時段錯誤!
printf("%c\n", p[0]);

原因: p 指向唯讀資料段,硬體記憶體保護機制禁止寫入。

正確姿勢: 使用字元陣列,內容會被複製到棧區(可讀寫)。

char arr[] = "hello world!\n"; // 棧上分配,內容可修改
arr[0] = 'H'; // 合法
printf("%s", arr); // 輸出 "Hello world!"

四、 字串作為函式參數:實為指標傳遞

void func(char *str) { /* ... */ }
func("hello"); // 字串字面量地址傳給 str

格式化字串漏洞示例:

char *p = "hello word! %d\n";
printf(p, 5); // 危險!若 p 來自用戶輸入,可導致格式化字串攻擊
// 安全寫法:printf("%s", p); 或 printf("hello word! %d\n", 5);

五、 字串長度計算:strlen vs sizeof

#include <string.h>
int main(void) {
    char *p = "hello";
    char a[] = "hello";
    
    printf("strlen(p) = %zu\n", strlen(p)); // 5,按 '\0' 計算有效長度
    printf("strlen(a) = %zu\n", strlen(a)); // 5
    printf("sizeof(p) = %zu\n", sizeof(p));  // 8 (64位指標大小)
    printf("sizeof(a) = %zu\n", sizeof(a));  // 6 (含 '\0' 的陣列總大小)
    return 0;
}

核心區別:

  • strlen:運行時遍歷計數,不包含結束符,僅適用於以'\0'結尾的字串。
  • sizeof:編譯期計算型別佔用字節數。指標型別大小固定(4/8字節),陣列型別為總大小(含結束符)。

六、 進階陷阱:指標越界與未定義行為

#include <unistd.h> // write
void func(void) {
    int *p = "hello!"; // 強制轉型,掩蓋編輯器警告
    write(1, p, 10); // 寫入 10 字節,超出字串長度,輸出後續記憶體垃圾值
}
int main(void) {
    int *q = "hello world";
    func();
    return 0;
}

教訓: C語言不做邊界檢查,越界言問導致未定義行為(輸出亂碼、崩潰、安全漏洞)。

總結:字串處理黃金法則

  1. 區分字串字面量(唯讀)與字元陣列(可讀寫)。
  2. 指標操作字串時,確保目標緩衝區足夠大且可寫。
  3. 優先使用strncpy、snprintf等帶長度限制的安全函式。
  4. 理解strlen(邏輯長度)與sizeof(佔用空間)的本質區別。
  5. 格式化字串永遠不直接使用用戶輸入作為格式控制參數。

掌握字串在記憶體中的真實面貌,是寫出健壯、安全C代碼的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved