• 操作系统:ubuntu22.04
  • IDE:Visual Studio Code
  • 编程语言:C++11

C/C++ 中 字符串字面量(string literal) 在不同上下文中的内存属性差异。下面我们从修改内容的角度详细解释两者的区别:


✅ 代码

char a[] = "hello";     // (1) 数组初始化
a[0] = 'X';             // ✅ 合法:可以修改

char *p = "world";      // (2) 指针指向字符串字面量
p[0] = 'X';             // ❌ 非法:修改只读内存 → 运行时崩溃(如 Segmentation Fault)

一、根本区别:内存存储位置与可写性

表达式 内存分配位置 是否可修改 原因
char a[] = "hello"; 栈(stack) ✅ 可修改 编译器将 "hello" 复制一份到栈上a 是一个可写的字符数组
char *p = "world"; 只读数据段(.rodata) ❌ 不可修改 "world" 存储在程序的只读内存区p 只是指向它的一个指针

二、深入解析

1. char a[] = "hello";

  • 这是一个数组定义 + 初始化
  • 编译器行为:
    • 在栈上分配 6 字节空间(5个字符 + \0);
    • 将字符串字面量 "hello" 的内容逐字节拷贝到这个栈空间;
  • 结果:a 是一个可读可写的局部数组,修改 a[0] 完全合法。

🔍 内存示意图:

栈区:
a: ['h', 'e', 'l', 'l', 'o', '\0']  ← 可修改

2. char *p = "world";

  • 这是一个指针变量初始化
  • 编译器行为:
    • 将字符串字面量 "world" 存入只读数据段(.rodata)
    • p 被赋值为该字符串的地址;
  • 结果:p 指向的是只读内存,任何尝试修改的行为都会导致未定义行为(Undefined Behavior),通常表现为 Segmentation Fault

🔍 内存示意图:

.rodata 段(只读):
"world\0"  ← 程序加载时写入,运行时不可改

栈区:
p: ────────→ 指向 .rodata 中的 "world"

三、为什么编译器不报错?

  • C/C++ 标准允许将字符串字面量赋给 char*(出于历史兼容性),但现代编译器会警告
  • 例如,用 gcc -Wall 编译:
    warning: deprecated conversion from string constant to ‘char*’
    
  • C++11 起已禁止:在 C++ 中,char *p = "world";编译错误,必须写成 const char *p = "world";

✅ 正确且安全的写法(C 和 C++ 通用):

const char *p = "world";  // 明确表示指向常量
// p[0] = 'X';  // 编译器直接报错!

四、总结对比表

特性 char a[] = "hello"; char *p = "world";
类型 char[6](数组) char*(指针)
内存位置 栈(可写) .rodata(只读)
内容来源 复制字符串字面量 直接指向字面量
能否修改内容 ✅ 能 ❌ 不能(运行时错误)
编译器检查 修改合法 修改不报错(但应警告)
推荐写法 无问题 应写为 const char *p = "world";

💡 最佳实践建议

  1. 永远不要试图修改字符串字面量
  2. 在 C++ 中,始终用 const char* 指向字符串字面量
  3. 在 C 中,也应主动加 const 提高安全性
  4. 如果需要可修改的字符串,要么用数组,要么动态分配:
    char *p = malloc(6);
    strcpy(p, "hello");
    p[0] = 'X';  // ✅ 安全
    free(p);
    

🌟 记住
“能通过编译” ≠ “能安全运行”
字符串字面量是常量,尊重它的只读属性,是写出健壮 C/C++ 程序的关键一步。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐