如果char * s是只读的,为什么我可以覆盖它们?

我的课程告诉我,char * s是静态/只读的,所以我认为这意味着你在定义之后就无法编辑它们。 但是当我跑步时:

char* fruit = "banana"; printf("fruit is %s\n", fruit); fruit = "apple"; printf("fruit is %s\n", fruit); 

然后编译好并给我:

 fruit is banana fruit is apple 

为什么? 我误解了只读是什么意思吗? 很抱歉,如果这是显而易见的,但我是新手编码,我无法在线找到答案。

显示的代码段不会更改字符串文字本身。 它只会更改指针fruit存储的值。

你可以想象这些线条

 char* fruit = "banana"; fruit = "apple"; 

以下方式

 char unnamed_static_array_banana[] = { 'b', 'a', 'n', 'a', 'n', 'a', '\0' }; char *fruit = &unnamed_static_array_banana[0]; char unnamed_static_array_apple[] = { 'a', 'p', 'p', 'l', 'e', '\0' }; fruit = &unnamed_static_array_apple[0]; 

这些语句不会更改与字符串文字对应的数组。

另一方面,如果你试图写

 char* fruit = "banana"; printf("fruit is %s\n", fruit); fruit[0] = 'h'; ^^^^^^^^^^^^^^ printf("fruit is %s\n", fruit); 

也就是说,如果您尝试使用指向它的指针(到字符串文字的第一个字符)更改字符串文字,则程序具有未定义的行为。

来自C标准(6.4.5字符串文字)

7如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的。 如果程序试图修改此类数组,则行为未定义。

在程序中,表达式"banana"表示程序映像中的字符串文字对象,即字符数组。 表达式的值是char *类型,或“指向字符的指针”。 指针指向该数组的第一个字节,即字符'b'

您的char *fruit变量也有类型“指向字符的指针”并从该表达式获取其初始值:它被初始化为指向数据的指针的副本,而不是数据本身; 它只是指向b

当你将"apple"分配给fruit ,你只是将其指针值替换为另一个,因此它现在指向不同的文字数组。

要修改数据本身,您需要一个表达式,例如:

 char *fruit = "banana"; fruit[0] = 'z'; /* try to turn "banana" into "zanana" */ 

根据ISO C标准,没有定义其行为。 可能"banana"数组是只读的,但这不是必需的。

C实现可以使字符串文字可写,或使其成为一个选项。

(如果你能够修改字符串文字,那并不意味着一切都很好。首先,你的程序仍然没有根据ISO C定义:它不可移植。其次,允许C编译器合并文字在同一个存储器中有共同的内容。这意味着程序中两次出现的"banana"实际上可能是完全相同的数组。此外,程序中某处出现的字符串文字"nana"可能是数组的后缀"banana"发生在别处;换句话说,共享相同的存储。修改文字可能会产生惊人的效果;修改可以出现在其他文字中。)

“静态”和“只读”也不是同义词。 C中的大多数静态存储实际上是可修改的。 我们可以创建一个可修改的静态字符数组,它包含如下字符串:

 /* at file scope, ie outside of any function */ char fruit[] = "banana"; 

要么:

 { /* in a function */ static fruit[] = "banana"; 

如果我们省略数组大小,它将从初始化字符串文字自动resize,并包含空终止字节的空间。 在函数中,我们需要static将数组放入静态存储,否则我们得到一个局部变量。

这些数组可以修改; fruit[0] = 'z'是明确定义的行为。

此外,在这些情况下, "banana"不表示字符数组。 arrays是可变的fruit ; "banana"表达式只是表示数组初始值的一段语法:

 char *fruit = "banana"; // "banana" is an object in program image // initial value is a pointer to that object char fruit_array[] = "apple"; // "apple" is syntax giving initial value 

基本上,当你表演时

 char* fruit = "banana"; 

你设置指针fruit到“香蕉”的第一个字母。 打印出来时,C基本上从’b’开始并保持打印字母,直到它在结尾处遇到\0 null字符。

到那时说

 fruit = "apple"; 

你已经改变了指针fruit ,现在指向“苹果”的第一个字母

首先, char*不是只读的。 char * const s是。 它们与char const *不同。 文字字符串(例如“香蕉”)应该是,但不一定。

 char * const cpfruit = "banana"; cpfruit = "apple"; // error char const * cpfruit = "banana"; cpfruit[0] = 'x'; // error char * ncfruit = "banana"; ncfruit[0] = 'x'; // compile will allow, but may cause run-time error. 

fruit对象是可写的 – 它可以设置为指向不同的字符串文字。

字符串文字 "banana""apple"不可写。 您可以修改fruit以指向字符串文字,但如果您这样做,则不应尝试修改fruit 指向的内容

 char *fruit = "banana"; // fruit points to first character of string literal fruit = "apple"; // okay, fruit points to first character of different string literal *fruit = 'A'; // not okay, attempting to modify contents of string literal fruit[1] = 'P'; // not okay, attempting to modify contents of string literal 

尝试修改字符串文字的内容会导致未定义的行为 – 您的代码可能会按预期工作,或者您可能会遇到运行时错误,或者可能发生完全意外的事情。 为安全起见,如果要定义一个指向字符串文字的变量,则应将其声明为const

 const char *fruit = "banana"; // can also be written char const * 

您仍然可以指定fruit来指向不同的字符串:

 fruit = "apple"; 

但如果你试图修改fruit指向的内容,编译器会对你大喊大叫。

如果要定义一个只能指向一个特定字符串文字的指针,那么您也可以对指针进行const限定:

 const char * const fruit = "banana"; // can also be written char const * const 

这样,如果您尝试写入指向哪个fruit ,或者尝试将fruit设置为指向不同的对象,编译器将对您大喊大叫。

您将可变fruit指向不同的字符串。 您只是覆盖了地址(位置)。 编译器将看到您的常量字符串“banana”和“apple”,并将它们分别存储在程序存储器中。 假设字符串“banana”转到位于地址1存储单元,“apple”存储到存储器addesss 2 。 现在你做的时候:

 fruit = "banana"; 

编译器只将1分配给变量fruit ,这意味着它指向包含字符串banana地址1 。 当你这样做时:

 fruit = "apple"; 

编译器将分配2变量fruit ,这意味着它指向存储字符串apple addess 2

你的课程教给你的是正确的!

当你首先定义char* fruit = "banana" ,你基本上会将fruit作为指向常量字符的指针。 字符串的7个字节(包括空终止)位于目标文件的.ro部分(部分名称显然会因平台而异)。

当您将char指针水果重置为“apple”时,它只是指向包含“apple”的只读部分中的另一个内存位置

基本上当你说水果是一个常数时,它指的是fruit是一个指向const记忆的指针。 如果你将它定义为const pointer to a const stringconst pointer to a const string : –
char* const fruit = "banana";
编译器会阻止你将它重置为“apple”