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


当前回答

不变的记忆

由于字符串字面量在设计上是只读的,所以它们存储在内存的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也可能在手动编译代码之前发现错误。

其他回答

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

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

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

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

char *str = "string";  

上面的代码将str设置为指向在程序的二进制映像中硬编码的字面值“string”,它在内存中可能被标记为只读。

因此str[0]=试图写入应用程序的只读代码。我猜这可能依赖于编译器。

要理解这个错误或问题,您应该首先了解指针和数组的差异b/w 所以在这里,我首先要解释一下它们的区别

字符串数组

 char strarray[] = "hello";

在存储器数组中存储的是连续存储器单元,存储为[h][e][l][l][o][\0] =>[]是1个char字节大小的存储器单元,而这个连续存储器单元可以通过名为strarray的名称在这里访问。这里string数组strarray本身包含初始化的所有字符串。在这种情况下,"hello" 因此,我们可以通过访问每个字符的索引值来轻松地更改其内存内容

`strarray[0]='m'` it access character at index 0 which is 'h'in strarray

它的值变成了m所以strarray的值变成了mello;

这里需要注意的一点是,我们可以通过一个字符一个字符地改变字符串数组的内容,但不能像strarray="new string"这样直接初始化其他字符串,这是无效的

指针

我们都知道指针指向内存中的内存位置, 未初始化的指针指向随机内存位置,初始化后指向特定内存位置

char *ptr = "hello";

这里的指针ptr被初始化为字符串“hello”,这是一个存储在只读存储器(ROM)中的常量字符串,所以“hello”不能被更改,因为它存储在ROM中

PTR存储在堆栈部分并指向常量字符串"hello"

所以ptr[0]='m'是无效的,因为你不能访问只读内存

但是ptr可以直接初始化为其他字符串值,因为它只是一个指针,所以它可以指向其数据类型变量的任何内存地址

ptr="new string"; is valid

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

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

char *str = "string";

分配一个指向字符串字面量的指针,编译器将其放入可执行文件中不可修改的部分;

char str[] = "string";

分配并初始化一个可修改的本地数组