ptr = "apple"; // shouldn't it be *ptr = "apple"
从头开始……
字符串字面量"apple" 存储在char 的6 元素数组中,如下所示:
+---+---+---+---+---+---+
|'a'|'p'|'p'|'l'|'e'| 0 |
+---+---+---+---+---+---+
结尾的0 标志着字符串的结束(它被称为字符串终止符)。
当“N-element array of T”类型的表达式出现在表达式中时,它会被转换(“decay”)为“pointer to T”类型的表达式和表达式的值将是数组第一个元素的地址,除非数组表达式是sizeof 或一元& 运算符的操作数,或者用于在声明中初始化字符数组。
因此,在声明中
ptr = "apple";
表达式"apple"从“char的6元素数组”类型的表达式转换(“衰减”)为“指向char的指针”。表达式ptr的类型是char *,或者“指向char的指针”;因此,在上面的赋值中,ptr 将收到"apple" 的第一个元素的地址。
应该不写成
*ptr = "apple";
因为表达式 *ptr 的计算结果是 ptr 指向的事物的值,此时它是 a) 不确定的,并且 b) 分配的类型错误。表达式*ptr的类型为char,与char *不兼容。
我编写了一个实用程序,可以打印内存中的项目映射;给定代码
char *ptr = "apple";
char arr[] = "apple";
地图看起来像这样:
Item Address 00 01 02 03
---- ------- -- -- -- --
apple 0x400c80 61 70 70 6c appl
0x400c84 65 00 70 74 e.pt
ptr 0x7fffcb4d4518 80 0c 40 00 ..@.
0x7fffcb4d451c 00 00 00 00 ....
arr 0x7fffcb4d4510 61 70 70 6c appl
0x7fffcb4d4514 65 00 00 00 e...
字符串文字 "apple" 位于地址 0x400c801。变量ptr 和arr 分别位于地址0x7fffcb4d4518 和0x7fffcb4d45102。
变量ptr 包含值0x400c80,它是"apple" 字符串文字的第一个元素的地址(x86 以“小端”顺序存储多字节值,因此最不重要字节在前,这意味着您必须从右到左阅读)。
还记得上面的“except”子句吗?在第二个声明中,字符串文字 "apple" 用于在声明中初始化 char 的 array;字符串文字的 contents 不是被转换为指针值,而是被复制到数组中,您可以在内存转储中看到。
printf("%s", ptr) // Why should I send the address instead of the value
因为这是 %s 转换说明符所期望的 - 它需要一个指向以 0 结尾的字符串的第一个字符的指针,并将打印出从该位置开始的字符序列,直到它看到终结者。
3 ...我不明白应该暗示什么
您不能更改数组对象的值。让我们看看str 在内存中的样子:
+---+
str: |'Q'| str[0]
+---+
|'u'| str[1]
+---+
|'e'| str[2]
+---+
|'s'| str[3]
+---+
|'t'| str[4]
+---+
| 0 | str[5]
+---+
您可以写入每个str[i]3(更改其值),但您不能写入str,因为没有可写入的内容。没有与数组元素分开的 str 对象。尽管 表达式 str 将“衰减”为指针值,但没有为该指针留出任何存储空间 - 转换是在编译时完成的。
同样,尝试修改字符串文字的内容会调用未定义的行为4;您可能会遇到段错误,或您的代码可能会按预期工作,或您可能会在列支敦士登发射核武器。所以你不能写信给*p或p[i];但是,您可以将新值写入p,将其指向不同的位置。
- 技术上是
0x0000000000400c80; %p 说明符去掉前导零。
- 同样的交易 - 从技术上讲,值是
0x000000007fffcb4d4518 和 0x000000007fffcb4d4510。请注意,特定的地址值将随运行而变化。
-
*str 等价于 str[0]
- C 语言定义标识了某些错误的操作,但并未对编译器提出任何要求以任何特定方式处理该代码。不同的平台以不同的方式存储字符串文字;有些将它们放在只读内存中,因此尝试修改它们会导致段错误,而其他平台将它们存储在可写段中以便操作成功。有些人可能会以不会出现段错误的方式存储它们,但字符串不会更改。