繁体   English   中英

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

[英]How does this piece of code determine array size without using sizeof( )?

通过一些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;
}

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

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

size = (&arr)[1] - arr;

所以我相信这两个问题都是有效的,并且对问题的解决方法略有不同。 谢谢大家的大力帮助和彻底的解释!

向指针添加1时,结果是指向类型的对象序列(即数组)中下一个对象的位置。 如果p指向int对象,则p + 1将指向序列中的下一个int 如果p指向int的5元素数组(在本例中为表达式&a ),那么p + 1将指向序列int的下一个5元素数组

减去两个指针(前提是它们都指向同一个数组对象,或者一个指向一个超过数组的最后一个元素)会产生这两个指针之间的对象数(数组元素)。

表达&a产生的地址a ,并且具有类型int (*)[5]指针至5个元素的数组的int )。 表达&a + 1产生的下一5个元素的数组的地址int以下a ,并且还具有的类型int (*)[5] 表达式*(&a + 1)解引用的结果&a + 1 ,使得其产生所述第一地址int的最后一个元素以下a ,并且具有类型int [5]在此上下文中,“衰减”到int *表达式。

类似地,表达a “衰减”的指针数组的第一元素和具有类型int *

图片可能会有所帮助:

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

+---+                   +---+
|   | <- &a             |   | <- a
| - |                   +---+
|   |                   |   | <- a + 1
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+
|   | <- &a + 1         |   | <- *(&a + 1)
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+

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

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

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

C 2011在线草案 ,6.5.6 / 9

这条线最重要:

size = *(&a + 1) - a;

正如你所看到的,它首先采取的地址a ,并增加了一个它。 然后,取消引用该指针和减去的原始值a从它。

C中的指针运算会导致返回数组中元素的数量,或者5 加入一种和&a是指向的5下一个阵列int年代后a 之后,此代码取消引用结果指针并从中减去a (已衰减为指针的数组类型),从而给出数组中的元素数。

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

假设你有一个指向xyz的指针,它指向一个int类型并包含值(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会得到下一个这样的对象
  • *有效地将该地址转换为int的类型指针
  • -a减去两个int指针,返回它们之间的int实例计数。

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM