下面的代码在第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测试。


当前回答

在第一个代码中,"string"是一个字符串常量,字符串常量永远不应该被修改,因为它们通常被放置在只读内存中。"str"是一个用来修改常量的指针。

在第二段代码中,"string"是一个数组初始化器,类似于

char str[7] =  { 's', 't', 'r', 'i', 'n', 'g', '\0' };

"str"是堆栈上分配的数组,可以自由修改。

其他回答

首先是一个不能修改的常量字符串。第二个是一个初始化值的数组,因此它可以被修改。

假设字符串是,

char a[] = "string literal copied to stack";
char *p  = "string literal referenced by p";

在第一种情况下,当'a'进入作用域时,文字将被复制。这里'a'是定义在stack上的数组。这意味着字符串将在堆栈上创建,其数据从代码(文本)内存中复制,通常是只读的(这是特定于实现的,编译器也可以将这种只读的程序数据放在可读写内存中)。

在第二种情况下,p是定义在堆栈(本地作用域)上的指针,并引用存储在其他位置的字符串字面量(程序数据或文本)。通常,修改这样的内存不是好的实践,也不鼓励。

这些答案大部分都是正确的,但为了更清楚一点……

人们所说的“只读内存”是ASM术语中的文本段。它是内存中加载指令的同一个地方。出于安全等明显的原因,这是只读的。当创建一个初始化为字符串的char*时,字符串数据被编译到文本段中,程序初始化指向文本段的指针。所以如果你想改变它,就死定了。段错误。

当作为数组编写时,编译器将初始化的字符串数据放在数据段中,这与全局变量等存在的位置相同。这个内存是可变的,因为数据段中没有指令。这一次,当编译器初始化字符数组(仍然只是一个char*)时,它指向的是数据段而不是文本段,您可以在运行时安全地更改文本段。

不变的记忆

由于字符串字面量在设计上是只读的,所以它们存储在内存的Constant部分。存储在那里的数据是不可变的,即不能被更改。因此,在C代码中定义的所有字符串字面值在这里都获得一个只读内存地址。

栈内存

内存的堆栈部分是存放局部变量地址的地方,例如,函数中定义的变量。


正如@matli的回答所暗示的,有两种方法来处理这些常量字符串。

1. 指向字符串字面量的指针

当我们定义指向字符串字面量的指针时,我们是在Stack内存中创建一个指针变量。它指向底层字符串字面值所在的只读地址。

#include <stdio.h>

int main(void) {
  char *s = "hello";
  printf("%p\n", &s);  // Prints a read-only address, e.g. 0x7ffc8e224620
  return 0;
}

如果我们试图通过插入来修改s

s[0] = 'H';

我们得到一个分割错误(核心转储)。我们试图访问不应该访问的内存。我们正在尝试修改只读地址0x7ffc8e224620的值。

2. 字符数组

对于示例而言,假设存储在常量内存中的字符串字面值“Hello”具有与上述地址相同的只读内存地址0x7ffc8e224620。

#include <stdio.h>

int main(void) {
  // We create an array from a string literal with address 0x7ffc8e224620.
  // C initializes an array variable in the stack, let's give it address
  // 0x7ffc7a9a9db2.
  // C then copies the read-only value from 0x7ffc8e224620 into 
  // 0x7ffc7a9a9db2 to give us a local copy we can mutate.
  char a[] = "hello";

  // We can now mutate the local copy
  a[0] = 'H';

  printf("%p\n", &a);  // Prints the Stack address, e.g. 0x7ffc7a9a9db2
  printf("%s\n", a);   // Prints "Hello"

  return 0;
}

注意:当使用指针指向字符串字面量时,如1。,最好的做法是使用const关键字,如const *s = "hello"。这样可读性更强,并且当它被违反时,编译器将提供更好的帮助。然后它将抛出类似error:分配只读位置' *s '的错误,而不是seg错误。编辑器中的linter也可能在手动编译代码之前发现错误。

因为在第一个例子的上下文中,“whatever”的类型是const char*(即使你将它赋值给一个非const char*),这意味着你不应该尝试写它。

编译器通过将字符串放在内存的只读部分来强制执行这一点,因此写入它会产生段错误。