C-这件代码如何在不使用sizeof()的情况下确定数组大小



浏览一些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 + 1a之后得出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。添加一个和&aa之后5 int s的下一个数组的指针。之后,此代码将结果指针减去a(数组类型已腐烂到指针(,从而给出数组中的元素数量。

有关指针算术如何工作的详细信息:

说您有一个指向int类型的指针xyz,并包含值(int *)160。当您从xyz中减去任何数字时,C指定从xyz中减去的实际金额是指指向类型的大小的数字倍。例如,如果您从xyz中减去5,则如果指针算术不适用,则xyz的值将为xyz - (sizeof(*xyz) * 5)

由于a5 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。我并不是说特定的编译器肯定会这样做,但可能会这样做。

最新更新