为什么我在写“a”时会出现分段错误;char*s";用字符串文字初始化,但不是;字符s[]”;?
以下代码接收到线路2上的seg故障:为什么我在写“a”时会出现分段错误;char*s";用字符串文字初始化,但不是;字符s[]”;?,c,segmentation-fault,c-strings,C,Segmentation Fault,C Strings,以下代码接收到线路2上的seg故障: char *str = "string"; str[0] = 'z'; // could be also written as *str = 'z' printf("%s\n", str); 虽然这样做效果很好: char str[] = "string"; str[0] = 'z'; printf("%s\n", str); 使用MSVC和GCC进行测试。,因为在第一个示例的上下文中,“whatever”的类型是常量字符*(即使您将其分配给非常量字符
char *str = "string";
str[0] = 'z'; // could be also written as *str = 'z'
printf("%s\n", str);
虽然这样做效果很好:
char str[] = "string";
str[0] = 'z';
printf("%s\n", str);
使用MSVC和GCC进行测试。,因为在第一个示例的上下文中,“whatever”
的类型是常量字符*
(即使您将其分配给非常量字符*),这意味着您不应该尝试对其进行写入
编译器通过将字符串放入内存的只读部分来实现这一点,因此写入它会生成segfault。通常,当程序运行时,字符串文本存储在只读内存中。这是为了防止意外更改字符串常量。在第一个示例中,
“string”
存储在只读内存中,*str
指向第一个字符。当您尝试将第一个字符更改为'z'
时,会发生SEGFULT
在第二个示例中,编译器将字符串“string”
从其只读主目录复制到str[]
数组。然后允许更改第一个字符。您可以通过打印以下地址来检查:
printf("%p", str);
此外,在第二个示例中打印str
的大小将显示编译器已为其分配了7个字节:
printf("%d", sizeof(str));
上述设置将str
指向文本值“string”
,该值在程序的二进制图像中硬编码,在内存中可能标记为只读
因此,str[0]=
正在尝试写入应用程序的只读代码。不过,我想这可能与编译器有关。在第一段代码中,“string”是一个字符串常量,字符串常量永远不应该被修改,因为它们通常被放在只读内存中。“str”是用于修改常量的指针
在第二段代码中,“string”是数组初始值设定项,有点像
char str[7] = { 's', 't', 'r', 'i', 'n', 'g', '\0' };
“str”是在堆栈上分配的数组,可以自由修改
char *str = "string";
分配一个指向字符串文字的指针,编译器将其放入可执行文件的不可修改部分
char str[] = "string";
分配和初始化可修改的本地数组字符串文字(如“String”)可能作为只读数据分配到可执行文件的地址空间中(给或取编译器)。当你去触摸它时,它会因为你在它的泳衣区而感到惊慌失措,并让你知道它有seg故障
在第一个示例中,您将获得指向该常量数据的指针。在第二个示例中,您正在使用常量数据的副本初始化一个由7个字符组成的数组。
char *str = "string";
行定义指针并将其指向文字字符串。文字字符串不可写,因此执行以下操作时:
str[0] = 'z';
你有seg故障。在某些平台上,文字可能位于可写内存中,因此您不会看到segfault,但无论如何它都是无效代码(导致未定义的行为)
该行:
char str[] = "string";
分配一个字符数组并将文本字符串复制到该数组中,该数组是完全可写的,因此后续更新没有问题。请参阅C常见问题解答
Q:这些初始化之间有什么区别?chara[]=“字符串文字”代码>
char*p=“字符串文字”代码>
如果我试图给p[I]
分配一个新值,我的程序就会崩溃
A:字符串文字(正式术语)
对于C中的双引号字符串
源代码)可在两种情况下使用
不同的方式:
作为字符数组的初始值设定项,正如在char a[]
的声明中一样,它指定初始值
该数组中的字符(和,
如有必要,其尺寸)
在其他任何地方,它都会变成一个未命名的静态字符数组,
这个未命名的数组可能会被存储
在只读存储器中,以及
因此不一定是
被改进的。在表达式上下文中,
该数组立即转换为
指针,和往常一样(见第6节),所以
第二个声明初始化p
指向未命名数组的第一个
元素李>
有些编译器有一个开关
控制是否使用字符串文本
是否可写(用于编译旧
代码),有些可以选择
使字符串文本形式化
视为常量字符数组(用于
更好的错误捕捉)
首先,str
是指向“string”
的指针。允许编译器将字符串文本放在内存中不能写入但只能读取的位置。(这确实应该触发一个警告,因为您正在将一个const char*
分配给一个char*
。您是禁用了警告,还是忽略了它们?)
其次,您正在创建一个数组,它是您可以完全访问的内存,并使用“string”
初始化它。您正在创建一个char[7]
(六个字母,一个用于终止'\0'),您可以随意使用它。与@matli链接的C FAQ提到了它,但这里还没有其他人提到过它,因此需要澄清:如果是字符串文字(源代码中的双引号字符串)用于初始化字符数组以外的任何位置(即:@Mark的第二个示例,它工作正常),该字符串由编译器存储在一个特殊的静态字符串表中,这类似于创建一个基本匿名的全局静态变量(当然是只读的)(没有变量“名称”)。只读部分是最重要的部分,这也是@Mark的第一个代码示例segfaults的原因。这些答案中的大多数都是正确的,只是为了更清楚一点
人们所指的“只读存储器”是ASM术语中的文本段。它与内存中加载指令的位置相同。这是只读的
char str[] = "string";
// create a string constant like this - will be read only
char *str_p;
str_p = "String constant";
// create an array of characters like this
char *arr_p;
char arr[] = "String in an array";
arr_p = &arr[0];
// now we try to change a character in the array first, this will work
*arr_p = 'E';
// lets try to change the first character of the string contant
*str_p = 'G'; // this will result in a segmentation fault. Comment it out to work.
/*-----------------------------------------------------------------------------
* String constants can't be modified. A segmentation fault is the result,
* because most operating systems will not allow a write
* operation on read only memory.
*-----------------------------------------------------------------------------*/
//print both strings to see if they have changed
printf("%s\n", str_p); //print the string without a variable
printf("%s\n", arr_p); //print the string, which is in an array.
char strarray[] = "hello";
`strarray[0]='m'` it access character at index 0 which is 'h'in strarray
char *ptr = "hello";
ptr="new string"; is valid
char c[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
char *c = "abc";
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
char *p = "abc";
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
char s[] = "abc";
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
readelf -l a.out
Section to Segment mapping:
Segment Sections...
02 .text .rodata
char a[] = "string literal copied to stack";
char *p = "string literal referenced by p";