下面的代码在第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测试。
下面的代码在第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测试。
当前回答
假设字符串是,
char a[] = "string literal copied to stack";
char *p = "string literal referenced by p";
在第一种情况下,当'a'进入作用域时,文字将被复制。这里'a'是定义在stack上的数组。这意味着字符串将在堆栈上创建,其数据从代码(文本)内存中复制,通常是只读的(这是特定于实现的,编译器也可以将这种只读的程序数据放在可读写内存中)。
在第二种情况下,p是定义在堆栈(本地作用域)上的指针,并引用存储在其他位置的字符串字面量(程序数据或文本)。通常,修改这样的内存不是好的实践,也不鼓励。
其他回答
参见C常见问题,问题1.32
Q: What is the difference between these initializations? char a[] = "string literal"; char *p = "string literal"; My program crashes if I try to assign a new value to p[i]. A: A string literal (the formal term for a double-quoted string in C source) can be used in two slightly different ways: As the initializer for an array of char, as in the declaration of char a[] , it specifies the initial values of the characters in that array (and, if necessary, its size). Anywhere else, it turns into an unnamed, static array of characters, and this unnamed array may be stored in read-only memory, and which therefore cannot necessarily be modified. In an expression context, the array is converted at once to a pointer, as usual (see section 6), so the second declaration initializes p to point to the unnamed array's first element. Some compilers have a switch controlling whether string literals are writable or not (for compiling old code), and some may have options to cause string literals to be formally treated as arrays of const char (for better error catching).
像“String”这样的字符串文字可能在可执行文件的地址空间中作为只读数据分配(通过编译器)。当你去触摸它时,它会害怕你在它的泳衣区,并让你知道一个隔离错误。
在第一个例子中,你得到一个指向const数据的指针。在第二个示例中,使用const数据的副本初始化一个7个字符的数组。
不变的记忆
由于字符串字面量在设计上是只读的,所以它们存储在内存的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也可能在手动编译代码之前发现错误。
要理解这个错误或问题,您应该首先了解指针和数组的差异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
The
char *str = "string";
Line定义了一个指针,并将其指向一个字面值字符串。字面值字符串是不可写的,所以当你这样做:
str[0] = 'z';
你会得到一个隔离失误。在某些平台上,字面值可能位于可写内存中,因此您不会看到段错误,但无论如何它都是无效代码(导致未定义的行为)。
线:
char str[] = "string";
分配一个字符数组并将字面值字符串复制到该数组中,该数组是完全可写的,因此后续更新没有问题。