你的两个例子没有可比性。
在你的第二个例子中,你有
char a[] = "abcdefg";
char *p = a;
所以a 是一个数组,p 是一个指针。在图片中绘制它看起来像
+---+---+---+---+---+---+---+---+
a: | a | b | c | d | e | f | g | \0|
+---+---+---+---+---+---+---+---+
^
|
+----|----+
p: | * |
+---------+
这一切都很好;该代码没有问题。
但在您的第一个示例中,在文件 1.c 中,您定义了一个名为 p 的数组:
+---+---+---+---+---+---+---+---+
p: | a | b | c | d | e | f | g | \0|
+---+---+---+---+---+---+---+---+
如果需要,您可以将数组命名为“p”(编译器当然不在乎),但是在文件0.c 中,您改变主意并声明p 是一个指针。您还声明(使用“extern”关键字)p 在其他地方定义。所以编译器相信你的话,并发出代码到位置p,并希望在那里找到一个指针——或者,在图片中,它希望找到一个包含箭头的框,指向其他地方。但它实际上发现的是你的字符串"abcdefg",只是它没有意识到这一点。它可能最终会尝试将字节 0x61 0x62 0x63 0x64(即构成字符串 "abcdefg" 的第一部分的字节)解释为指针。显然这行不通。
如果将0.c中的printf调用改为
printf("%p\n", p);
这会将指针 p 的值打印为指针。 (嗯,当然,p 并不是真正的指针,但你对编译器撒谎并告诉它它是,所以你会看到编译器将它视为指针时的结果,这就是我们试图在这里理解。)在我的系统上打印
0x67666564636261
这是字符串"abcdefg\0"的全部8个字节,以相反的顺序排列。 (由此我们可以推断出我在一台(a)使用 64 位指针并且(b)是 little-endian 的机器上。)所以如果我尝试打印
printf("%c\n", p[3]);
它会尝试从位置0x67666564636264(即0x67666564636261 + 3)获取一个字符并打印它。现在,我的机器有相当多的内存,但它没有太多,所以位置0x67666564636264 不存在,因此程序在尝试从那里获取时崩溃。
还有两件事。
如果数组和指针不一样,你是怎么做到的
char *p = a;
在你的第二个例子中,我说的是“一切都好;没有问题”?
如何将右侧的数组分配给左侧的指针?
答案是著名的(臭名昭著的?)“C 中数组和指针之间的等价性”:实际发生的事情就像你说过的一样
char *p = &a[0];
每当你在表达式中使用数组时,你得到的实际上是一个指向数组第一个元素的指针,正如我在这个答案的第一张图片中展示的那样。
当你问“为什么它不起作用,而它在这里起作用?”时,你可以用另外两种方式问它。
假设我们有两个函数
void print_char_pointer(char *p)
{
printf("%s\n", p);
}
void print_char_array(char a[])
{
printf("%s\n", a);
}
然后假设我们回到你的第二个例子,
char a[] = "abcdefg";
char *p = a;
假设我们调用
print_char_pointer(a);
或
print_char_array(p);
如果您尝试一下,您会发现其中任何一个都没有问题。
但这怎么可能呢?我们如何将数组传递给
当我们调用 print_char_pointer(a)? 时需要指针的函数?
以及我们如何将指针传递给
需要一个数组的函数,当我们调用print_char_array(p)?
记住,每当我们在表达式中提到数组时,
我们得到的是指向数组第一个元素的指针。所以当
我们叫
print_char_pointer(a);
我们得到的就像我们写的一样
print_char_pointer(&a[0]);
实际上传递给函数的是一个指针,它是
函数期望什么,所以我们很好。
但是在另一种情况下,我们将一个指针传递给一个声明为接受数组的函数呢?好吧,“C 中的数组和指针之间的等价性”实际上还有另一个原则。
我们写的时候
void print_char_array(char a[])
编译处理它就像我们写的一样
void print_char_array(char *a)
为什么编译器会做这样的事情?为什么,因为它知道
没有数组会被传递给函数,所以它知道没有
函数实际上会收到一个数组,所以它知道
函数将接收一个指针。所以就是这样
编译器会处理它。
(而且,很清楚,当我们谈论“等价
在 C 中的数组和指针之间”,我们并不是说
指针和数组是等价的,只是有这个
它们之间存在特殊的等价关系。我提过
该等价的两个原则已经。这里都有
其中三个,供参考:(1)每当你
在表达式中提到数组的名称,你
自动获取的是指向数组第一个元素的指针。
(2) 每当你声明一个似乎接受
数组,它实际上接受的是一个指针。 (3) 每当你
在指针上使用“数组”下标运算符[],如
p[i],你实际得到的就像你写了*(p + i)。而且,事实上,如果你仔细考虑一下,由于
原则 (1),即使您使用数组下标运算符
看起来像一个数组的东西,你实际上是在一个
指针。但这是一个很奇怪的概念,你不知道
如果您不想这样做,则不必担心,因为它确实有效。)