“Arrays = Pointers”*心灵狂热*。 许多年后:“实际上,他们没有”*心灵再次爆炸*

语境:

我在C中打开和关闭编程大约2年,然后发现a[i]只是*(a + i)语法糖,因此相当于* (i + a)i[a] 。 我的现实被颠倒了,很多都是“AHA!” 在接下来的几天研究和阅读之后出现了启示时刻(“那就是为什么数组总是通过引用传递!”等)。 从那时起,我已经内化了指针/数组的等价并使它紧贴我的心脏,所以想象一下,当我偶然发现这个名为“arrays衰变”的东西时,这是多么的震撼。 这是典型的例子:

码:

 #include  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 

题:

事实certificate,C毕竟对数组有一些认识! 在声明数组的main中,程序能够正确地报告它的大小。 现在我想知道有多少数组语法只是指针操作的语法糖(以前我曾假设:所有这些)。 C实际上确实有数组,它们的局限性是什么? 这个例子表明只要你处于相同的function就可以获得它们的长度,你可以做些什么呢? 在这个腐烂的东西开始之前你能走多远?

“旧”C语言中有两个运算符不触发数组类型衰减: sizeof运算符和一元运算符。 sizeof计算整个数组的大小(而不是指针大小),而&返回指针指向数组类型的指针(不是指向指针类型的指针)。 正如埃里克在评论中指出的那样,C99增加了_Alignof

有时还会提到另外一个上下文:使用字符串文字初始化char数组(即字符串文字不会衰减到指针)。

也可以这样说:在对象上下文 (AKA左值上下文)中,数组保留其“数组”,而在值上下文 (AKA右值上下文)中,它们会立即衰减为指针。

PS作为历史评论:C语言的一个祖先–B语言 – 实际上实现了数组作为物理指针,这意味着B中的每个数组实际上都是一个指向独立分配的内存块的指针。 最初假设此实现也将转移到C。 但是,C必须具有struct类型。 并且B样式的数组创建了不必要的复杂性,将数组作为struct对象的成员。 他们会使struct对象的初始化变得非常简单, struct对象将变成不可复制的原始memcpy等。这在C中被认为是不可接受的。因此,数组被重新设计为它们当前的forms。 C数组不是指针,但他们仍然模仿他们的祖父母从B语言的指针式行为,这经常混淆人们学习C.

(有关完整故事,请参见http://cm.bell-labs.com/cm/cs/who/dmr/chist.html 。)

传递给函数时,数组的大小“丢失”。 正如你所指出的那样, sizeof是一个编译时的东西,看到了“真正的”大小。 这可以起作用,因为sizeof根本不是一个函数,因为你可以通过使用它而没有括号来certificate(例如sizeof arr ,虽然奇怪的sizeof some_type不合法C)。

说数组和指针是“等价的”意味着它们既不相同也不可互换。 这意味着它的指针算法和数组索引在C中是等价的,指针和数组是不同的。

对表达式中出现的array-of-T类型的对象的引用会衰减为指向其第一个元素的指针; 结果指针的类型是指向T的指针。 也就是说,只要数组出现在表达式中,编译器就会隐式生成指向数组第一个元素的指针,就像程序员编写了&a[0]

sizeof& operator是此规则的例外。

另外,我推荐专家C编程一书,它使用整章来解释指针和数组的差异和混淆。

将数组声明为局部变量或全局变量是指针与指针的不同之处。 int arr[100]; 分配400个字节的空间,而int *p只分配4.你可以使用类似于数组的int *p局部变量,如果你为它设置一些空间,比如

 int *p = (int *)malloc(100 * sizeof(int)); 

当然,你还必须记得以后释放那些记忆。 您还可以随意将声明为int arr[100]的局部变量传递给其forms参数类型为int *的函数。 说到函数参数,数组和指针更相同。

 i[a] 

不,如果我是一个int,这将无法工作。 但其他三种forms是等价的。