快速回答:在这两种情况下,传递给change 函数的是指针值(不是指针对象)。该指针值是target 数组的第一个元素的地址。在第一个示例中,change 函数修改了该指针指向的数组。在第二个示例中,change 函数修改了指针。具体来说,它修改了函数的指针的本地副本,对调用者没有影响。
//Herbert Schildt 的引用//
这是你的第一个问题。 Herbert Schildt 写了几本关于 C 的书,但他真的不太了解这门语言。阅读由真正的 C 专家撰写的他的书的一些评论:The Annotated Annotated C Standard,由 Clive D.W. 撰写。 Feather 和 C: The Complete Nonsense,由 Peter "Seebs" Seebach 撰写。
我不知道你的例子来自哪本书。他对int main(void)的使用是正确的,可能表明这是他后来的著作之一;在他早期的书中,他使用了void main(void),这是明显不正确的(或至少无缘无故地不可移植)。
找一本更好的书。
在你的第一个例子中:
void change(char *tar);
...
char target[80]="hello";
change(target);
参数tar是一个指针。那个指针没有改变; change 函数更改指针指向的内容。 target 是一个数组对象,但在 C 语言中有一条特殊规则,即在大多数情况下,数组类型的表达式会隐式转换为指向数组第一个元素的指针。 (例外情况是当数组是一元 & 或 sizeof 运算符的操作数,或者当它是用于初始化数组对象的初始化程序中的字符串文字时;这些都不适用。)
所以在调用change(target) 时,传递给change 函数的参数是指向数组元素target[0] 的指针(等效地,地址);调用change(&target[0]) 将完全等效。通过指针运算,该函数不仅可以更改指针指向的char 对象,还可以更改属于同一数组元素的其他char 对象。 ([] 索引运算符是根据指针算法定义的。)
顺便说一下,这个循环:
for(i=0;i<strlen(tar);i++)
/* ... */
效率极低。它在循环的每次迭代中重新计算strlen,并且每次重新计算都会遍历整个字符串。
第二个示例中的change 函数完全不同:
void change(char *tar)
{
int i;
tar="aaaaa";
}
赋值复制的是指针值,而不是数组值。 (C 中没有数组赋值;这就是为什么我们有像strcpy 和memcpy 这样的函数。)tar 和第一个例子一样,是指针类型的参数。参数是一个局部变量,它从调用者传递的相应参数中获取其初始值。赋值修改了该局部变量(因此它指向与字符串字面量关联的数组),但它对调用者传入的值没有影响。
最后,在这两个例子中,主程序都是这样做的:
printf("%s",target);
(顺便说一句,它应该是"%s\n",而不仅仅是'%s"。)计算表达式target的结果没有改变;它是数组的基地址,只要数组存在,它就是固定的。那么为什么输出会改变呢?因为%s 格式说明符与任何其他printf 格式说明符不同,打印的不是相应参数的值,而是它所指向的值。
建议阅读:comp.lang.c FAQ 的第 6 节,标题为“数组和指针”。这是我所知道的用于解释 C 中数组和指针之间经常令人困惑的关系的最佳资源。