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

jan*_*lic 130 c arrays size pointer-arithmetic language-lawyer

通过一些C面试问题,我找到了一个问题,指出“如何在不使用sizeof运算符的情况下在C中查找数组的大小?”,并提供以下解决方案。它有效,但是我不明白为什么。

#include <stdio.h>

int main() {
    int a[] = {100, 200, 300, 400, 500};
    int size = 0;

    size = *(&a + 1) - a;
    printf("%d\n", size);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如预期的那样,它返回5。

编辑:人们指出了这个答案,但是语法确实有所不同,即索引方法

size = (&arr)[1] - arr;
Run Code Online (Sandbox Code Playgroud)

因此,我认为这两个问题都是有效的,并且对该问题的解决方法略有不同。谢谢大家的大力帮助和详尽的解释!

Joh*_*ode 133

当您将1加到一个指针时,结果是下一个对象在指向类型的对象序列(即数组)中的位置。如果p指向一个int对象,p + 1则将指向int序列中的下一个对象。如果p指向的5元素数组int(在这种情况下为&a),p + 1则将指向序列中的下一个5元素数组int

减去两个指针(假设它们都指向同一个数组对象,或者一个指针指向数组的最后一个元素),则得出这两个指针之间的对象(数组元素)数。

该表达式&a产生的地址a,并具有类型int (*)[5](指向的5元素数组的指针int)。表达&a + 1产生的下一个5个元素的数组的地址int以下a,并且还具有的类型int (*)[5]。该表达式*(&a + 1)取消引用的结果&a + 1,从而产生int最后一个元素之后的第一个地址a,并具有type int [5],在这种情况下,它会“衰减”为type的表达式int *

类似地,表达式a“衰减”为指向数组第一个元素的指针,并具有type int *

图片可能会有所帮助:

int [5]  int (*)[5]     int      int *

+---+                   +---+
|   | <- &a             |   | <- a
| - |                   +---+
|   |                   |   | <- a + 1
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+
|   | <- &a + 1         |   | <- *(&a + 1)
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+
Run Code Online (Sandbox Code Playgroud)

这是同一存储的两个视图-在左侧,我们将其视为5个元素的数组序列int,而在右侧,我们将其视为的序列int。我还展示了各种表达式及其类型。

请注意,该表达式会*(&a + 1)导致未定义的行为

...
如果结果指向数组对象的最后一个元素之后,则不应将其用作被评估的一元*运算符的操作数。

C 2011在线草案,6.5.6 / 9

  • 正式使用“不得使用”文本:C 2018 6.5.6 8。 (13认同)
  • 因此,如果有人写了`size =(int *)(&a + 1)-a;`,那么此代码将完全有效?:o (7认同)

JL2*_*210 34

这条线是最重要的:

size = *(&a + 1) - a;
Run Code Online (Sandbox Code Playgroud)

如您所见,它首先获取地址a并添加一个。然后,它取消引用该指针并a从中减去其原始值。

C语言中的指针算术使它返回数组或中的元素数5。加1和&a是指向int之后5 秒的下一个数组的指针a。之后,此代码取消对结果指针的引用a,并从中减去(已衰减为指针的数组类型),得到数组中元素的数量。

有关指针算法工作原理的详细信息:

假设你有一个指针xyz,它指向的int类型和包含的价值(int *)160。当从中减去任何数字时xyz,C表示从中减去的实际数量xyz是该数字乘以它所指向的类型的大小。例如,如果5从中减去xyz,则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
Run Code Online (Sandbox Code Playgroud)

这意味着代码a&a[5](或a+5)中减去,得到5

请注意,这是未定义的行为,在任何情况下都不应使用。不要期望此行为在所有平台上都一致,也不要在生产程序中使用它。


Gem*_*lor 27

嗯,我怀疑这在C的早期是无法解决的。但是它很聪明。

一次执行一个步骤:

  • &a 获取指向类型为int [5]的对象的指针
  • +1 假设存在这些对象的数组,则获取下一个此类对象
  • * 有效地将该地址转换为指向int的类型指针
  • -a 减去两个int指针,返回它们之间的int实例数。

考虑到正在进行的某些类型操作,我不确定这是否完全合法(在这里我是指语言律师合法-不会在实践中起作用)。例如,当两个指针指向同一数组中的元素时,只允许它们相减。*(&a+1)是通过访问另一个数组(虽然是父数组)来合成的,因此实际上不是指向与相同数组的指针a。同样,虽然允许您合成指针到数组的最后一个元素之后,并且您可以将任何对象都视为1个元素的数组,但是*在此合成指针上“允许” 取消引用()操作,即使它在这种情况下没有任何行为!

我怀疑在C的早期(K&R语法,有人吗?),数组*(&a+1)会更快地衰减为指针,因此,它们可能只返回int **类型的下一个指针的地址。现代C ++的更严格的定义肯定允许存在指向数组类型的指针并知道数组的大小,并且可能C语言标准也紧随其后。所有C函数代码仅将指针作为参数,因此技术上的可见差异很小。但是我只是在这里猜测。

这种详细的合法性问题通常适用于C解释器或lint类型的工具,而不是编译后的代码。解释器可能将2D数组实现为指向数组的指针的数组,因为要实现的运行时功能较少,在这种情况下,对+1的取消引用将是致命的,即使执行+1也会给出错误的答案。

另一个可能的弱点可能是C编译器可能会对齐外部数组。想象一下,如果这是一个5个字符的数组(char arr[5]),则在程序执行&a+1时会调用“数组数组”行为。编译器可能会决定char arr[][5]实际生成5个字符数组的数组作为8个字符数组的数组char arr[][8],以便外部数组很好地对齐。我们正在讨论的代码现在将报告数组大小为8,而不是5。我并不是说特定的编译器肯定会这样做,但是可以。

  • 它不是有效的@ JL2210,因为未定义的行为是通过评估一元`*`表达式引起的(并且仅出于该原因)。如果将该操作替换为类型为int *的强制转换,则情况不太清楚。 (16认同)
  • 大多数人都这样做。例如,`sizeof(array)/ sizeof(array [0])`给出数组中元素的数量。 (5认同)
  • 编译器无法将char [] [5]实现为char arr [] [8]。数组只是其中的重复对象。没有填充。另外,这将破坏C 2018 6.5.3.4 7中的(非规范)示例2,该示例告诉我们我们可以使用`sizeof array / sizeof array [0]`计算数组中的元素数量。 (5认同)
  • 指针减法不仅限于同一数组中的两个指针,还允许指针超出数组末尾。定义了“&amp;a+1”。正如 John Bollinger 所指出的,`*(&amp;a+1)` 不是,因为它试图取消引用不存在的对象。 (2认同)