一、两种写法的直观对比
我们先看两个最常见的字符串定义:
char s[] = "hello"; // 字符数组 char *p = "hello"; // 字符串指针从表面上看,二者都能通过printf("%s", s)和printf("%s", p)打印出hello,但它们的底层存储位置、修改权限完全不同:
char s[]:在栈上分配数组空间,字符串内容会被拷贝到数组中,可以修改。char *p:指针变量保存在栈上,指向只读的字符串常量区,强行修改会导致程序崩溃。
二、字符数组:拥有独立存储空间
字符数组的本质是在栈上开辟一块连续的内存空间,将字符串常量的内容拷贝到这块空间中。因此,数组拥有这块内存的所有权,可以自由修改其中的字符。
示例代码
#include <stdio.h> int main() { char s[] = "hello"; s[0] = 'H'; // 可以修改数组中的字符 printf("%s\n", s); // 输出:Hello return 0; }底层逻辑
- 栈上分配6字节空间(
hello\0)。 - 字符串常量
"hello"的内容被拷贝到数组中。 - 数组名
s是这块内存的首地址,修改s[0]会直接修改数组中的内容。
三、字符串指针:指向只读常量区
字符串指针的本质是在栈上分配一个指针变量,保存字符串常量的首地址。而字符串常量本身存储在只读的常量区,不允许修改。
错误示例(会导致崩溃)
#include <stdio.h> int main() { char *p = "hello"; p[0] = 'H'; // 危险!修改只读常量区,程序会崩溃 printf("%s\n", p); return 0; }底层逻辑
- 栈上分配8字节(64位系统)的指针变量
p。 - 字符串常量
"hello"存储在只读的常量区,p保存其首地址。 - 尝试修改
p[0]会触发段错误,因为常量区的内存是只读的。
四、核心差异对比表
| 特性 | char s[] = "hello" | char *p = "hello" |
|---|---|---|
| 存储位置 | 栈上数组空间 | 栈上指针 + 常量区字符串 |
| 所有权 | 数组拥有内容所有权 | 指针仅保存地址,无所有权 |
| 修改权限 | 可以自由修改 | 禁止修改,修改会崩溃 |
| 适用场景 | 需要修改字符串内容 | 仅需读取字符串内容 |
五、正确使用建议
1. 需要修改字符串时,优先使用字符数组
如果后续需要修改字符串内容,必须使用字符数组,并提前分配足够的空间:
#include <stdio.h> #include <string.h> int main() { // 分配20字节空间,足够容纳修改后的字符串 char s[20] = "hello"; s[0] = 'H'; strcat(s, " C"); // 拼接字符串 printf("%s\n", s); // 输出:Hello C return 0; }2. 仅需读取字符串时,可使用指针
如果只是需要读取字符串内容,使用指针更节省栈空间:
#include <stdio.h> int main() { const char *p = "hello"; // 加const更安全,防止误修改 printf("%s\n", p); // 输出:hello return 0; }六、一句话总结
字符数组和字符串指针的核心差异,不在于能否打印字符串,而在于谁拥有这块字符空间:
- 数组有自己的存储空间,内容可以修改;
- 指针仅保存常量区的地址,内容禁止修改。
在实际开发中,务必根据需求选择合适的写法,避免因混淆二者导致程序崩溃。