改自水木社区帖子
数组不是指针。 数组定义 char a[6] 请求预留 6 个字符的位置, 并用名称 ``a" 表示。也就是说, 有一个称为 ``a" 的位置, 可以放入 6 个字符。 而指针申明 char *p, 请求一个位置放置一个指针, 用名称 ``p" 表示。 这个指针几乎可以指向任何位置: 任何字符和任何连续的字符, 或者哪里也不指.
一个图形胜过千言万语。声明
char a[6] = "hello";
char *p = "world";
p的类型char *,指向一个常量字符串。
p初始化为一个指针值,指向一个内存区域,该处有6个字符的数据,即'w', 'o', 'r', 'l', 'd', '\0'。 在运行过程中,p的值可改变,指向其他任何允许的地址。但上面的数据("world")不会在程序退出之前销毁,也不可修改![这是另外一个比较迷惑人的细节],即使p变量生命周期结束.
a的类型是array of char,初始化为6个字符的数组,也是'h', 'e', 'l', 'l', 'o', '\0'。在运行过程中,a的内容可改变。
将会初始化下图所示的数据结果:
+---+---+---+---+---+---+
a: | h | e | l | l | o |\0 |
+---+---+---+---+---+---+
+-----+ +---+---+---+---+---+---+
p: | *======> | w | o | r | l | d |\0 |
+-----+ +---+---+---+---+---+---+
根据 x 是数组还是指针, 类似 x[3] 这样的引用会生成不同的代码。认识到这一点大有裨益。以上面的声明为例, 当编译器看到表达式 a[3] 的时候, 它生成代码从 a 的位置开始跳过 3 个, 然后取出那个字符. 如果它看到 p[3], 它生成代码找到 ``p" 的位置, 取出其中的指针值, 在指针上加 3 然后取出指向的字符。换言之, a[3] 是 名为 a 的对象 (的起始位置) 之后 3 个位置的值, 而 p[3] 是 p 指向的对象的 3 个位置之后的值. 在上例中, a[3] 和 p[3] 碰巧都是 'l' , 但是编译器到达那里的途径不尽相同。本质的区别在于类似 a 的数组和类似 p 的指针一旦在表达式中出现就会按照不同的方法计算, 不论它们是否有下标。
那么, 在 C 语言中 ``指针和数组等价" 到底是什么意思 ?
在 C 语言中对数组和指针的困惑多数都来自这句话。说数组和指针 ``等价" 不表示它们相同, 甚至也不能互换。它的意思是说数组和指针的算法定义可以用指针方便的访问数组或者模拟数组。
特别地, 等价的基础来自这个关键定义:
一个 T 的数组类型的左值如果出现在表达式中会蜕变为一个指向数组第一个成员的指针(除了三种例外情况); 结果指针的类型是 T 的指针。
这就是说, 一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了 &a[0] 一样。例外的情况是, 数组为 sizeof 或 & 操作符的操作数, 或者为字符数组的字符串初始值。
作为这个这个定义的后果, 编译器并那么不严格区分数组下标操作符和指针。在形如 a[i] 的表达式中, 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如 p[i] 那样寻址,尽管最终的内存访问并不一样。 如果你把数组地址赋给指针:
p = a;
那么 p[3] 和 a[3] 将会访问同样的成员。
为什么容易给人造成类型指针与类型数组名可等价的疑惑呢?虽然类型不同,但C规定(为了
追求简洁与灵活性,C假设使用者知道自己代码会有什么结果。)在很多场合下,认为数组名
与类型指针类型兼容。记忆中只有2中情况下,数组名不可等同视为数组指针,&与sizeof操作符。
void foo1(const char *str) {...};
void foo2(int size) {return size;};
...
char *s1 = "hello";
char s2[6] = "hello";
foo1(s1); // ok
foo1(s2); // ok
foo1(&s2); // incompatible
foo2(s2[0]); // ok
s1[0] = '0'; // error
s2[1] = s1[0]; //ok
s2[0] = '0'; // ok
s1 = s2; // ok
s2 = s1; // error
// 下面假设在ia32平台上运行
foo2(sizeof(s1)); // return 4, pointer size
foo2(sizeof(s2)); // return 6, array size
PS:我的练习代码
- #include <stdio.h>
-
-
int main(void)
-
{
-
char *s1 = "world";
-
char s2[6] = "hello";
-
-
printf("%c\n%s\n", s1[3], s2);
-
-
s2[0] = s1[0];
-
s2[4] = s1[4];
-
-
printf("%c\n%s\n", s1[3], s2);
-
return(0);
-
}