上下文:
我在C中打开和关闭编程大约2年,之后发现a[i]
只是*(a + i)
的语法糖,因此相当于* (i + a)
和i[a]
。我的现实被颠倒了,很多都是“AHA!”在接下来的几天学习和阅读(“那就是为什么数组总是通过引用传递!”等)之后的启示时刻。从那时起,我已经内化了指针/数组的等价并将其保持在我心中,所以想象一下,当我偶然发现这个名为“数组衰变”的东西时,这是多么可怕的震撼。这是典型的例子:
代码:
#include <stdio.h>
int Length(int*);
int main () {
int arr[100];
printf("Length of array: %d\n",(int)(sizeof(arr)/sizeof(arr[0])));
printf("Length of array: %d\n",Length(arr));
return 0;
}
int Length(int arr[]) {
return sizeof(arr)/sizeof(arr[0]);
}
结果:
Length of array: 100
Length of array: 2
问题:
事实证明,C毕竟对数组有一些认识!在声明数组的main中,程序能够正确地报告它的大小。现在我想知道有多少数组语法只是指针操作的语法糖(以前我假设:所有这些)。 C实际上确实有数组,它们的局限性是什么?这个例子表明只要你处于相同的功能就可以获得它们的长度,你能做些什么呢?在这种腐烂的事情发生之前你能走多远?
答案 0 :(得分:6)
“旧”C语言中有两个运算符不会触发数组类型衰减:sizeof
运算符和一元&
运算符。 sizeof
计算整个数组的大小(而不是指针大小),而&
返回指针指向数组类型的指针(不是指向指针类型的指针)。正如埃里克在评论中指出的那样,C99添加了_Alignof
。
有时还会提到另外一个上下文:使用字符串文字初始化char数组(即字符串文字不会衰减为指针)。
也可以这样说:在对象上下文(AKA左值上下文)数组保留其“数组”,而在值上下文(AKA右值上下文)中立即腐烂到指针。
PS 作为历史评论:C语言的一个祖先--B语言 - 确实将数组实现为物理指针,这意味着B中的每个数组实际上都是一个指向独立分配的指针记忆块。最初假设此实现也将转移到C。但是,C必须具有struct
种类型。并且B样式数组使得数组成为struct
对象的成员而产生了不必要的复杂性。他们会使struct
个对象初始化非平凡,struct
对象将变为不可复制的原始memcpy
等。这在C中被认为是不可接受的。因此,数组被重新设计为他们目前的形式。 C数组不是指针,但它们仍然模仿来自B语言的祖父母的类似指针的行为,这常常让学习C的人感到困惑。
(有关完整故事,请参阅此处http://cm.bell-labs.com/cm/cs/who/dmr/chist.html。)
答案 1 :(得分:3)
传递给函数时,数组的大小“丢失”。正如您所指出的那样,sizeof
作为一个编译时的东西,看到了“真实”的大小。这可以起作用,因为sizeof
根本不是一个函数,因为你可以通过使用它而没有括号来证明(例如sizeof arr
,尽管奇怪的是sizeof some_type
不合法C)。
答案 2 :(得分:2)
说数组和指针是“等价的”意味着它们既不相同也不可互换。这意味着它的指针算法和数组索引在C,指针和数组中是等价的。
对表达式中出现的array-of-T类型的对象的引用会衰减为指向其第一个元素的指针;结果指针的类型是指向T的指针。也就是说,只要数组出现在表达式中,编译器就会隐式生成指向数组第一个元素的指针,就像程序员编写了&a[0]
一样。
sizeof
或& operator
是此规则的例外情况。
另外,我推荐书Expert C Programming,它使用整章解释指针和数组的差异和混淆。
答案 3 :(得分:-1)
将数组声明为局部变量或全局变量是指针与指针的不同之处。 int arr[100];
分配400个字节的空间,而int *p
仅分配4个。如果你为某个空格malloc,可以使用类似于数组的int *p
局部变量,例如
int *p = (int *)malloc(100 * sizeof(int));
当然,您还必须记得在以后释放该记忆。您还可以随意将声明为int arr[100]
的局部变量传递给其形式参数类型为int *
的函数。说到函数参数,数组和指针稍微等一点。
i[a]
不,如果我是一个int,这将无效。但其他三种形式是等价的。