浏览一些C面试问题,我找到了一个问题,说明"如何在不使用sizeof操作员的情况下找到C中的数组大小?",以及以下解决方案。它有效,但我不明白为什么。
#include <stdio.h>
int main() {
int a[] = {100, 200, 300, 400, 500};
int size = 0;
size = *(&a + 1) - a;
printf("%dn", size);
return 0;
}
正如预期的,它返回5。
编辑:人们指出了这个答案,但是语法确实有些不同,即索引方法
size = (&arr)[1] - arr;
因此,我相信两个问题都是有效的,并且在问题上都有略有不同的方法。谢谢大家的巨大帮助和详尽的解释!
当您将1添加到指针中时,结果是下一个对象的位置,指向类型的对象(即数组(。如果p
指向int
对象,则p + 1
将以序列指向下一个int
。如果p
指向int
的5个元素阵列(在这种情况下,&a
(,则p + 1
将指向序列中的int
的下一个 5-元素阵列。
减去两个指针(前提是它们都指向同一数组对象,或者一个指向一个数组的最后一个元素(会在这两个指针之间产生对象数(数组元素(的数量。
表达式&a
产生a
的地址,并具有int (*)[5]
类型(指向int
的5个元素阵列的指针(。表达式&a + 1
在a
之后得出int
的下一个5个元素数组的地址,并且具有int (*)[5]
类型。表达式*(&a + 1)
表示&a + 1
的结果,因此它在a
的最后一个元素之后产生了第一个int
的地址,并且具有int [5]
类型,在此上下文中"到类型int *
的表达。
同样,表达式 a
'decays;指向数组的第一个元素的指针,并具有类型int *
。
图片可能会有所帮助:
int [5] int (*)[5] int int *
+---+ +---+
| | <- &a | | <- a
| - | +---+
| | | | <- a + 1
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
+---+ +---+
| | <- &a + 1 | | <- *(&a + 1)
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
+---+ +---+
这是同一存储的两个视图 - 在左侧,我们将其视为int
的5个元素阵列的序列,而在右侧,我们将其视为int
的序列。我还显示了各种表达式及其类型。
请注意,表达式*(&a + 1)
导致未定义的行为:
...
如果结果指向一个数组对象的最后一个元素,则不应用作评估的单一 *操作员的操作数。
c 2011在线草稿,6.5.6/9
这一行是最重要的是:
size = *(&a + 1) - a;
如您所见,它首先采用a
的地址,并在其中添加一个。然后,它说出指针并从中减去a
的原始值。
C中的指针算术导致此返回数组中的元素数量或5
。添加一个和&a
是a
之后5 int
s的下一个数组的指针。之后,此代码将结果指针减去a
(数组类型已腐烂到指针(,从而给出数组中的元素数量。
有关指针算术如何工作的详细信息:
说您有一个指向int
类型的指针xyz
,并包含值(int *)160
。当您从xyz
中减去任何数字时,C指定从xyz
中减去的实际金额是指指向类型的大小的数字倍。例如,如果您从xyz
中减去5
,则如果指针算术不适用,则xyz
的值将为xyz - (sizeof(*xyz) * 5)
。
由于a
是5
int
类型的数组,因此结果值将为5。但是,这仅适用于指针,而仅适用于数组。如果您使用指针尝试此操作,则结果将始终为1
。
这是一个示例,显示了地址以及如何不确定。左侧显示地址:
a + 0 | [a[0]] | &a points to this
a + 1 | [a[1]]
a + 2 | [a[2]]
a + 3 | [a[3]]
a + 4 | [a[4]] | end of array
a + 5 | [a[5]] | &a+1 points to this; accessing past array when dereferenced
这意味着代码正在从&a[5]
(或a+5
(中减去a
,给出5
。
请注意,这是不确定的行为,不应在任何情况下使用。不要期望这一行为在所有平台上都保持一致,并且不要在生产程序中使用它。
嗯,我怀疑这是在C的早期就无法做到的。
一次采取步骤:
-
&a
获取指向INT类型的对象[5] -
+1
假设有一个数组
,获得了下一个这样的对象 -
*
有效地将地址转换为类型指针 -
-a
减去两个int指针,返回它们之间的int实例计数。
我不确定它是完全合法的(在这个我的意思是语言律师合法的 - 鉴于某些类型的操作正在进行中,它在实践中是否有效(。例如,当您指出同一数组中的元素时,您只能"允许"两个指针。*(&a+1)
是通过访问另一个数组(尽管是父阵列(来合成的,因此实际上并不是与a
相同数组的指针。另外,虽然您可以在数组的最后一个元素上合成指针,并且您可以将任何对象视为1个元素的数组,但在此综合指针上不``允许''dereferctrencing(*
(的操作,即使在这种情况下,它没有行为!
我怀疑在C(k&amp; r语法,有人吗?(的早期,一个数组腐烂到指针中的速度更快,因此*(&a+1)
只能返回下一个INT **类型指针的地址。现代C 的更严格的定义绝对使指针具有数组类型的存在并知道数组大小,并且C标准可能效仿了。所有C功能代码仅将指针作为参数,因此技术可见差异很小。但是我只是在这里猜。
这种详细的合法性问题通常适用于C解释器或棉类型工具,而不是编译的代码。解释器可能会实现2D数组作为数组的一系列指针,因为要实现的运行时功能较小,在这种情况下,将 1删除将是致命的,即使它起作用也会给出错误的答案。
另一个可能的弱点可能是C编译器可能对齐外阵列。想象一下,如果这是5个字符的数组(char arr[5]
(,则当程序执行&a+1
时,它正在调用"数组数组"行为。编译器可能会决定,实际上生成了5个字符(char arr[][5]
(的数组数组作为8个字符的数组(char arr[][8]
(,因此外部阵列很好地对齐。我们正在讨论的代码现在将数组大小报告为8,而不是5。我并不是说特定的编译器肯定会这样做,但可能会这样做。