Steve Summit..
13
你的两个例子没有可比性.
在你的第二个例子中,你有
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")解释为指针.显然这不起作用.
你可以清楚地看到这一点,如果你改变了printf呼叫0.c到
printf("%p\n", p);
这会将指针的值打印p 为指针.(当然,p这不是一个真正的指针,但你对编译器说谎并且告诉它它是,所以当编译器将它视为指针时,你会看到的结果,这就是我们所说的试图在这里理解.)在我的系统上打印
0x67666564636261
这是字符串的所有8个字节,"abcdefg\0"顺序相反.(从这里我们可以推断出我在一台机器上,(a)使用64位指针而(b)是小端.)所以如果我试图打印
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),甚至当你使用数组下标操作的东西,看起来像一个数组,你真正使用它的一个指针.但是,这是一个非常奇怪的想法,如果你不想,你不必担心,因为我 只是工作.)