char *str = "hello";
str[0] = 'H'; // 试图修改只读内存中的字符串
注意:上面代码中,str
是指向字符串字面量 "hello"
的指针。因为字符串字面量通常被存储在只读存储区域中,所以 str[0] = 'H';
试图修改这块只读内存会导致段错误。
1、字符串字面量存储在只读区域
C语言中,字符串字面量(例如 "hello"
)在程序运行时通常存储在只读数据段。也就是程序可以读取这些字面量,但是不能对其进行修改。使用字符指针 char *str = "hello";
会将指针 str
指向这个只读内存区域。
当尝试修改这个字面量(例如 str[0] = 'H';
),程序会尝试写入一个只读的内存区域,操作系统会检测到这一点并触发段错误。
2、常量字符串和字符数组的区别
当声明 char *str = "hello";
时,str
指向的是只读的字符串字面量。但是,如将字符串赋值给一个字符数组,字符串内容将被复制到栈上分配的内存中,此时可以安全地修改数组中的内容。
// 字符数组,字符串内容存储在可修改的内存中
char str[] = "hello";
str[0] = 'H'; // 可以修改
printf("%s\n", str); // 输出 "Hello"
3、出现段错误的原因
段错误(Segmentation Fault)通常表示程序试图访问一个不允许访问的内存区域,可有访问未初始化或已经释放的指针。也可能写入只读的内存区域(例如尝试修改字符串字面量)。还能可能访问越界的数组等。出现这个错误可能有多种原因。在这个特定问题中,段错误发生的原因是试图修改一个指向只读内存的字符串字面量。
4、解决方法
要避免段错误,应该确保字符串字面量被分配到可写的内存中,通常可以使用字符数组来实现。
1)使用字符数组
#include <stdio.h>
int main() {
char str[] = "hello"; // 分配可修改的数组
str[0] = 'H'; // 修改是安全的
printf("%s\n", str); // 输出 "Hello"
return 0;
}
2)动态分配内存
如想使用指针并能够修改字符串,可以通过动态分配内存来创建字符串。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// 为字符串分配内存("hello" 长度为 5,加上 '\0' 共 6 字节)
char *str = malloc(6);
// 复制字符串到动态分配的内存中
strcpy(str, "hello");
str[0] = 'H'; // 修改字符串
printf("%s\n", str); // 输出 "Hello"
free(str); // 释放分配的内存
return 0;
}
注意:使用 malloc()
为字符串动态分配内存,然后使用 strcpy()
复制字符串到这块可修改的内存区域。这样就可以安全地修改字符串,并且需要在使用完之后释放这块内存以防止内存泄漏。
5、其它段错误情况
段错误(Segmentation Fault) 是C语言中常见的运行时错误,表示程序尝试访问未分配的内存或没有权限访问的内存区域。当程序试图进行非法的内存访问操作时,操作系统会强制终止程序并抛出段错误。除上面修改字符串字面量导致段错误,还可能有其它情况。
1)访问未初始化的指
未初始化的指针指向的是随机内存地址,访问或修改它会导致段错误。
#include <stdio.h>
int main() {
int *ptr; // 未初始化的指针
*ptr = 100; // 试图访问未初始化的指针,导致段错误
return 0;
}
2)访问已经释放的指针(悬空指针)
使用free()
释放了动态分配的内存后,指针仍然持有该地址,但它不再指向有效的内存区域。继续使用这个指针会导致段错误。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
*ptr = 100;
printf("Before free: %d\n", *ptr);
free(ptr); // 释放内存
*ptr = 200; // 试图访问已释放的内存,导致段错误
return 0;
}
3)数组越界访问
访问数组时,如访问了超过数组范围的元素,就会出现段错误。
#include <stdio.h>
int main() {
int arr[3] = {1, 2, 3};
printf("%d\n", arr[3]); // 越界访问,arr[3] 超过了数组的边界
return 0;
}
4)递归调用过深(栈溢出)
递归函数如果没有适当的终止条件,可能会导致栈溢出,从而引发段错误。
#include <stdio.h>
void recursive() {
int arr[1000]; // 占用栈内存
recursive(); // 无限递归调用,最终导致栈溢出
}
int main() {
recursive(); // 调用递归函数
return 0;
}