C语言字符串声明与内存分配:字面量存储及修改异常问题
核心问题解答:str1指向的字符存储位置及不可修改原因
当你写char* str1 = "This is a string.";时,字符串字面量本身存储在程序的只读数据段(通常标记为.rodata),这是程序静态存储区的一部分,在编译阶段就被确定并写入可执行文件中。而str1这个指针变量本身是存储在栈上的(从你打印的&str1地址与栈变量stack_var地址接近可以验证),它仅负责指向只读数据段里的字符串内容。
操作系统会给只读数据段设置不可写的内存访问权限,目的是保护程序中的常量数据不被意外修改。一旦你尝试通过str1[5] = '\0';修改这块内存,就会触发内存访问违规,也就是你遇到的Bus error。
三种字符串声明的内存对比
结合你的代码和地址输出,三种声明的内存分布完全不同:
栈上数组(
str2)char str2[17] = "This is a string.";是在栈上直接分配了17字节的数组空间,然后把字符串字面量的内容拷贝到这个栈数组中。栈内存属于程序的可写区域,所以修改str2的元素不会有任何问题。从地址看,str2的0x7ff7be6f5870和栈变量stack_var的地址接近,验证了这一点。堆内存(
heap_str)malloc(18)在堆上申请了一块动态内存,strcpy把字符串字面量的内容拷贝到这块堆内存中。堆内存是程序动态申请的可写区域,因此修改heap_str指向的内容也不会报错。heap_str的0x600002129120地址和栈、只读段地址差距明显,符合堆内存的分布特征。只读数据段(
str1指向的内容)str1指向的0x10180df66地址既不在栈也不在堆,就是只读数据段的地址。这个区域的内容是只读的,任何修改操作都会触发系统的内存保护机制。
额外建议
为了在编译阶段就避免误修改字符串字面量的问题,建议把指向字面量的指针声明为const char* str1 = "This is a string.";。这样当你尝试修改str1指向的内容时,编译器会直接抛出错误,而不是等到运行时才触发Bus error,能更早发现问题。
内容的提问来源于stack exchange,提问作者chvolow24

