没有任何地址规范的字符指针如何保存数据?

How can a character pointer without any address specification hold data?

根据我对指针的理解,以下 C 程序不应该工作,但它确实可以工作。

#include<stdio.h>
main() {
    char *p;
    p = "abcdefghijk";
    printf("%s", p);
}

输出:

abcdefghijk

char 指针变量 p 指向随机的东西,因为我没有像 p = &i; 那样给它分配任何地址,其中 i 是一些 char 数组。

这意味着如果我尝试向指针 p 持有的内存地址写入任何内容,它应该会给我分段错误,因为它是一些随机地址,未由 OS 分配给我的程序.

但是程序编译运行成功。发生了什么事?

在 C 中,像 "abcdefghijk" 这样的字符串文字实际上存储为(只读)字符数组。该赋值使 p 指向该数组的第一个字符。


我注意到您提到了 p = &i,其中 i 是一个数组。这在大多数情况下是错误的。数组自然会衰减为指向其第一个元素的指针。 IE。做 p = i 等于 p = &i[0].

虽然 &i&i[0] 会产生相同的地址,但在语义上却大不相同。让我们举个例子:

char array[10];

通过上面的定义 &array[0](或者只是简单的 array,就像上面解释的那样),你得到一个指向 char 的指针,即 char *。当执行 &array 时,你会得到一个指向十个字符数组的指针,即 char (*)[10]。两种类型差别很大。

在这个表达式语句中

p="abcdefghijk";

指针p被赋值为字符串文字"abcdefghijk"的第一个字符的地址,编译器将其作为零终止字符数组存储在静态内存区中。

因此在这个语句中发生了两件事。首先,编译器创建一个具有静态存储持续时间的未命名字符数组来保存字符串文字。然后将数组第一个字符的地址赋值给指针。你可以这样想象

char unnamed[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', '[=11=]' };
p = unnamed;

p = &unnamed[0]; 

请注意,尽管 C 中的字符串文字具有非常量字符数组类型,而 C++ 中它们具有常量字符数组类型,但您不能更改字符串文字。任何更改字符串文字的尝试都会导致未定义的行为。

所以这段代码无效

char *p = "abcdefghijk";
p[0] = 'A';

但是您可以创建自己的字符数组并使用字符串文字对其进行初始化,在这种情况下您可以更改该数组。例如

char s[] = "abcdefghijk";
char *p = s;
p[0] = 'A';

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

7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

注意这部分的引用

It is unspecified whether these arrays are distinct provided their elements have the appropriate values.

意思是如果你要写

char *p = "abcdefghijk";
char *q = "abcdefghijk";

那么这个表达式不一定会产生 true(整数值 1)

p == q

并且结果取决于编译器选项是将相同的字符串文字存储为一个数组还是不同的数组。

p="abcdefghijk";

您正在代码段中创建一个字符串文字,并将文字的第一个字符的地址分配给指针,由于指针不是常量,您可以用不同的地址再次分配它。

字符串文字 "abcdefghijk" 是通过将字符放入程序的 datatext 段的块中编译的。那么你把它赋值给指针就是把它在数据段中的位置地址赋值给指针。

"abcdefghijk"是一个字符串常量,p="abcdefghijk";会给p这个字符串的地址。 所以printf("%s",p);正常显示这个字符串没有错误。