为什么我在写“a”时会出现分段错误;char*s";用字符串文字初始化,但不是;字符s[]”;?

为什么我在写“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”的类型是常量字符*(即使您将其分配给非常量字符

以下代码接收到线路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”
的类型是
常量字符*
(即使您将其分配给非常量字符*),这意味着您不应该尝试对其进行写入


编译器通过将字符串放入内存的只读部分来实现这一点,因此写入它会生成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";