C语言中修改共享地址的字符指针触发段错误,原因何在?
为什么修改共享地址的字符串字面量会触发段错误?
嘿,你观察到的str1和str2地址相同,是因为编译器的字符串池(string pooling)优化——当多个字符指针指向完全相同的字符串字面量时,编译器会让它们共享同一个内存地址,避免重复存储相同的字符串,帮程序节省内存空间。
但这里踩了一个C语言里很常见的坑:字符串字面量是存储在只读数据段(read-only data segment)中的,操作系统会把这块内存标记为「只读」,不允许程序修改它。当你尝试通过*(str2+2)='O'去写这块内存时,就违反了内存的读写权限,操作系统会直接抛出段错误(Segmentation Fault)来终止程序,保护只读内存不被非法篡改。
具体拆解你的代码问题:
- 字符串字面量的存储位置:
"Sudhanshu"这种字符串字面量,在编译阶段就会被放到程序的只读数据区(比如Linux系统下的.rodata段),这块内存只能读取,绝对不能写入。 - 指针指向的是只读内存:你写的
char *str1="Sudhanshu";,其实是让str1这个指针指向了只读数据区里的"Sudhanshu",而不是可写的栈内存或者堆内存。 - 共享地址的原因:编译器的字符串池优化会把相同的字符串字面量合并成一个,所以
str1和str2自然指向同一块只读内存地址。 - 修改触发错误的本质:当你调用
update(str2)尝试修改*(str2+2)时,本质是在往只读内存里写数据,这直接触发了操作系统的内存保护机制,所以程序会崩掉。
怎么解决这个问题?
如果你需要修改字符串,得把字符串字面量复制到可写的内存区域里,常见的两种方式:
1. 使用栈上的字符数组
把字符串字面量复制到栈上的数组中,数组所在的栈内存是可写的:
#include<stdio.h> void update(char *str2){ *(str2+2)='O'; } int main(){ char str1[]="Sudhanshu"; // 字面量被复制到栈数组,可写 char str2[]="Sudhanshu"; // 每个数组都是独立的栈内存,地址不同 printf(" %s , %s ",str1,str2); update(str2); printf(" %s , %s ",str1,str2); if(str1==str2){ printf("True\n"); }else printf("False\n"); return 0; }
2. 使用堆内存分配
通过strdup或者malloc+strcpy把字符串复制到堆内存,堆内存也是可写的:
#include<stdio.h> #include<stdlib.h> #include<string.h> void update(char *str2){ *(str2+2)='O'; } int main(){ char *str1=strdup("Sudhanshu"); // 复制字面量到堆内存,可写 char *str2=strdup("Sudhanshu"); printf(" %s , %s ",str1,str2); update(str2); printf(" %s , %s ",str1,str2); if(str1==str2){ printf("True\n"); }else printf("False\n"); // 记得用完堆内存要释放,避免内存泄漏 free(str1); free(str2); return 0; }
最后再补个小提醒:用==比较char*指针,其实是在比较指针的内存地址,而不是字符串的内容。如果要判断两个字符串的内容是否相同,应该用标准库的strcmp()函数哦。
内容的提问来源于stack exchange,提问作者Sudhanshu Patel
相关产品推荐
相关产品推荐

