使用指针运算获取数组的长度

7
我不认为这是重复的(见下文)。
我发现了一个非常相似的问题,但我认为答案没有分析漏洞。请参阅: Is `*((*(&array + 1)) - 1)` safe to use to get the last element of an automatic array?
我知道通常的方法是计算sizeof(array)/sizeof(*array)来获取数组中元素的数量。(并且有人在评论中指出C++有std::size(array)。)
因此,我认为如果我能够到达最后一个元素之后的地址,那么这个数字可以通过指针数学运算计算出来。但是,然后我想到我可以:
&array + 1

但是,这个值的类型是指向数组的指针,因此为了让数学计算起作用,我需要对这个值进行解引用。
const auto N = *(&array + 1) - array;

在C和C++中是否有一种允许这种解引用合法的津贴?

我知道你可以指向数组的最后一个元素,但是你不允许对其进行解引用。然而,这个解引用产生了一个数组对象,它立即衰减为指向其第一个元素的指针。

我可以使用reinterpret_cast来避免使用解引用运算符,但我想知道C和/或C++语言是否允许在这种情况下使用解引用。


我错过了一个类似的问题,但这个问题有些微妙的不同。我已经理解了代码的工作原理,并且我知道在数组的最后一个元素之后解引用指针通常会出现问题。我想知道数组对象在表达式中使用时会衰减为指针的事实是否允许例外。我认为可能会,因为解引用指向对象的指针通常会导致使用对象值本身在表达式中(如果对象不存在,则会出现问题)。


评论不适合进行长时间的讨论;此对话已被移至聊天室 - Samuel Liew
2个回答

4
不行。这样做是未定义的行为:

请注意,表达式 *(&a + 1) 的结果是未定义的行为:

...

如果结果指向数组对象的最后一个元素以外的位置,则不得将其用作计算的一元 * 运算符的操作数。

C 2011 在线草案, 6.5.6/9

所以试图超出边界解引用会导致未定义的行为。没有例外。
编辑:我可能是错的。有另一个来源对此问题表示,您可以对其进行取消引用(强调我自己):

[注意:例如,数组结尾之后的地址(5.7)将被视为指向该地址可能位于的与数组元素类型无关的其他对象。 —注解]

在我看来,这似乎意味着是的,您可以合法地解除引用它,但读取或写入到该位置的结果是未指定的。

因此,因为它是指针的第1个过去的位置(根据这位作者),所以解引用它是可以的,只是不能读取或写入结果。在这种情况下,这不会影响您,因为您正在使用其大小属性。
但请记住,任何超过结束(而不是开始)的1个以上的内容都不起作用。
实际上,这是有争议的,甚至可能是由委员会本身争议。虽然我应该注意到,此处链接的问题略有不同于此问题。

我还没有对任何答案采取任何行动(除了这个评论)。我理解这个立场,只是不确定它是否真实。 - jxh
我不明白。为什么这个引用不适用? - user10957435
1
如果我要假装成一名语言律师,我可能会认为在解引用指向数组的指针时,被评估的那部分可能是一个漏洞。 - jxh
@jxh 我可能错了,你是对的。你觉得我的编辑怎么样? - user10957435
我需要再考虑一下我想要提出什么问题作为后续。谢谢你的回答。 - jxh
看起来你比我更好地找到了答案。但是不管怎样,你仍然是受欢迎的。 - user10957435

1

是的,C和C++都有一些东西可以使这个想法合法。

一个对象(包括数组对象)之后的指针可以用于指针比较和算术运算,即使它不能被解引用。你想做的是创建一个指向array之后的int[5]的指针,将该指针转换为int*(现在指向array[N-1]),然后减去&array[0]

然而,你的“解引用”实现存在问题。在array之后没有实际的第二个数组。你的解引用根本不必要;你只需要使用它来触发从int[N]int*的隐式转换。但是你可以使用显式转换代替,(int*)(&array+1)

[C++17措辞,强调是我的]

为了进行指针算术运算(5.7表达式添加)和比较(5.9表达式.rel,5.10表达式.eq),将数组x的最后一个元素之后的指针视为指向虚构元素x[n]的指针。请保留HTML标签。

1
在问题的最后一段讨论并驳回了“转换”解决方案。 - user3386109
但是您可以使用显式转换来代替。这样会有什么帮助呢?(int*)(&array+1) - array 会产生未定义行为。 - Language Lawyer
@LanguageLawyer:假设array是一个int数组或者适当对齐于int *,它就不会有未定义的行为。C 2018 6.3.2.3 7说,对象类型的指针可以转换为不同对象类型的指针。如果结果没有适当对齐,那么行为是未定义的。否则,在转换回来时,它应该与原始值相等。然而,这就是它所说的全部内容,因此在这个答案中使用(int *)(&array+1) - &array[0]并没有被指定为有效。 - Eric Postpischil
@EricPostpischil回答者引用了C++17标准,(int*)(&array+1) - array在C++17中具有未定义行为。 - Language Lawyer
谢谢你的回答。我想更深入地探讨指针算术表达式为什么会是未定义的。 - jxh
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接