C 崩溃或“崩溃”;分段故障“;将数据复制/扫描/读取到未初始化的指针时
本问题旨在作为以下性质的所有常见问题的参考: 当我将数据复制/扫描到未初始化指针指向的地址时,为什么会出现神秘的崩溃或“分段错误” 例如:C 崩溃或“崩溃”;分段故障“;将数据复制/扫描/读取到未初始化的指针时,c,pointers,segmentation-fault,C,Pointers,Segmentation Fault,本问题旨在作为以下性质的所有常见问题的参考: 当我将数据复制/扫描到未初始化指针指向的地址时,为什么会出现神秘的崩溃或“分段错误” 例如: char* ptr; strcpy(ptr, "hello world"); // crash here! 或 指针是一种特殊类型的变量,它只能包含另一个变量的地址。它不能包含任何数据。您不能“将数据复制/存储到指针中”-这没有任何意义。您只能将指针设置为指向其他位置分配的数据 这意味着为了使指针有意义,它必须始终指向有效的内存位置。例如,它可以指向堆栈上
char* ptr;
strcpy(ptr, "hello world"); // crash here!
或
指针是一种特殊类型的变量,它只能包含另一个变量的地址。它不能包含任何数据。您不能“将数据复制/存储到指针中”-这没有任何意义。您只能将指针设置为指向其他位置分配的数据 这意味着为了使指针有意义,它必须始终指向有效的内存位置。例如,它可以指向堆栈上分配的内存:
{
int data = 0;
int* ptr = &data;
...
}
或在堆上动态分配的内存:
int* ptr = malloc(sizeof(int));
在指针初始化之前使用它总是一个错误。它尚未指向有效内存
这些示例都可能导致程序崩溃或其他类型的意外行为,例如“分段错误”:
相反,您必须确保指针指向(足够)分配的内存:
/*** examples of correct use of pointers ***/
// 1.
int var;
int* good = &var;
*good = 42;
// 2.
char* good = malloc(5 + 1); // allocates memory for 5 characters *and* the null terminator
strcpy(good, "hello");
请注意,您还可以通过让指针指向
NULL
,将指针设置为指向定义良好的“无处”。这使得它成为一个空指针,这是一个保证不指向任何有效内存的指针。这与完全未初始化指针不同
int* p1 = NULL; // pointer to nowhere
int* p2; // uninitialized pointer, pointer to "anywhere", cannot be used yet
但是,如果试图访问空指针指向的内存,可能会遇到与使用未初始化指针类似的问题:崩溃或分段错误。在最好的情况下,您的系统会注意到您试图访问空地址,然后抛出“空指针异常”
空指针异常错误的解决方案是相同的:在使用指针之前,必须将指针设置为指向有效内存
进一步阅读: 指向无效数据的指针
分段错误及其原因
strcpy
希望您传递指向两个字符数组的两个指针(第一个指针不能是常量),如以下签名:
char * strcpy ( char * destination, const char * source );
示例用法:
“%[^\t\n]s”
(制表符,换行符)或“%[^\t\n]s”
(空格,制表符,换行符))
(在现实生活中,不要忘记检查
scanf()
!)中的返回值)这是因为您没有为指针
char*ptr
分配内存。
在这种情况下,必须为指针动态分配内存
两个函数malloc()
和calloc()
可用于动态内存分配
请尝试以下代码:-
char* ptr;
ptr = malloc(50); // allocate space for 50 characters.
strcpy(ptr, "hello world");
当*ptr
的使用结束时,不要忘记释放分配给*ptr
的内存。这可以使用free()
函数完成
free(ptr); // deallocating memory.
可以使用realloc()
更改动态分配内存的大小
在大多数情况下,“分段错误”是由于内存分配错误或数组超出范围而发生的。学习C时经常出现的一种情况是尝试使用单引号表示字符串文字:
char ptr[5];
strcpy(ptr, 'hello'); // crash here!
// ^ ^ because of ' instead of "
在C语言中,'h'
是一个单字符文字,而“h”
是一个字符串文字,包含一个'h'
和一个空终止符\0
(即一个2字符数组)。此外,在C语言中,字符文字的类型是int
,也就是说,sizeof('h')
相当于sizeof(int)
,而sizeof(char)
是1
char h = 'h';
printf("Size: %zu\n", sizeof(h)); // Size: 1
printf("Size: %zu\n", sizeof('h')); // likely output: Size: 4
为了制作字符串的可修改副本,POSIX C库没有使用
malloc
、strlen
和strcpy
,而是在
中有一个名为strdup
的简便函数,该函数将返回传入的以空结尾的字符串的副本,并分配存储持续时间。使用后,指针应在空闲时释放:
char* ptr;
ptr = strdup("hello world");
ptr[0] = 'H';
puts(ptr);
free(ptr);
这类错误通常是由尚未掌握指针是什么或它们如何工作的初学者编写的。因此,请注意,这个社区wiki的目的是保持基本的解释水平。如果您希望留下更多关于C标准等的高级答案,请针对该问题发布不同的答案。“它不能包含任何数据。”-嗯,实际上地址就是它的数据。@Olaf请在此保留基本内容:)这是为初学者准备的。虽然。。。如果地址是数据,那么为什么CPU既有地址总线又有数据总线?它从地址总线读取指针变量的内容吗?;-)(例如,PCIe使用数据包/命令格式,没有经典的地址/数据总线机制)。也有这样的RAM设计(还记得RAMBUS吗?)无论如何,我对答案很满意,应该足以满足初学者。如果他们有兴趣深入了解,只需留下注释即可。注意最后一个示例:不需要取消引用指针来显示未定义的行为。与其他任何内容一样,不确定的内容(p2
的值的情况)本质上会在评估时调用未定义的行为,更不用说通过取消引用而进一步疯狂了。是的,这是一个不同的问题,但密切相关。总之,“在指针初始化之前使用它总是一个错误”这句话是正确的,但是“使用”不仅仅限于取消引用。问题更多的是OPs甚至不知道指针是未初始化的,而是一旦你声明/定义(他们也混淆了这一点)指针,就会神奇地出现一个对象
char* ptr;
ptr = malloc(50); // allocate space for 50 characters.
strcpy(ptr, "hello world");
free(ptr); // deallocating memory.
char *tmp = realloc(ptr, 100); // allocate space for 100 characters.
if (! tmp) {
// reallocation failed, ptr not freed
perror("Resize failed");
exit(1);
}
else {
// reallocation succeeded, old ptr freed
ptr = tmp;
}
char ptr[5];
strcpy(ptr, 'hello'); // crash here!
// ^ ^ because of ' instead of "
char h = 'h';
printf("Size: %zu\n", sizeof(h)); // Size: 1
printf("Size: %zu\n", sizeof('h')); // likely output: Size: 4
char* ptr;
ptr = strdup("hello world");
ptr[0] = 'H';
puts(ptr);
free(ptr);