众所周知,如果没有运算符,sizeof则可以计算数组元素的个数,例如,像这样:
int arr[10];
size_t size = *(&arr + 1) - arr;
在哪里
arr是指向第一个元素的指针
&arr有一个指向整个数组的指针
&arr + 1在我们的数组之后有一个指向下一块内存的指针
*(&arr + 1)是数组最后一个元素之后的元素的地址。
因此,指针的差异给出了它们之间的元素数量。
问题:
这不会
*(&arr + 1)导致UB?如何
&arr有指向整个数组的指针?它是由标准定义的吗?
*(&arr + 1)可以写成(&arr)[1]或1[&arr]。正是以这种“更有趣”的形式,这个问题在讨论中定期出现。在 C++ 中,这个问题没有正式的答案。该话题一度被积极讨论,但仍停留在“草拟”状态:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
尚未看到对这个问题给出明确答案的尝试。也就是说,直到现在,在 C++ 语言中,对于是否有可能做到这一点的老问题还没有答案
至少第一种选择的形式合法性问题自豌豆之王时代就已为人所知,但尚未提供合理的答案。
在 C 语言中,已经尝试通过声明相邻的运算符
&并*在计算表达式之前相互“消灭”彼此来解决其中的一些情况。这使该选项合法化&arr[10]但这并没有正式使变体
1[&arr](您的变体)合法化。在这个变体中,我们有与 C 语言声明相邻
&和*“消灭”彼此的方式相同,有必要*声明相邻的“隐式数组到指针转换”和运算符“折叠”为简单的指针转换,即 认为这个表达式等价然而,这还没有完成。也就是说,在 C 中,您的版本正式生成未定义的行为。
在 C++ 语言中,一切都还悬而未决(并且长期以来一直悬而未决)。
按照标准,& 运算符获取操作数的地址,并将其实际转换为一个元素的数组。
更简单的地址算术。在这种情况下,数组元素是一个由十个整数元素组成的数组。因此,递增指针将移动到数组中的下一个元素。在那之后,只剩下一个指向下一个元素的第一个元素的指针(对不起重言式)并得到它们的区别。命名
*(&arr + 1)将给出一个指向下一个数组的第一个元素的指针int arr[10];,但实际上指针将保持不变,只是类型int*。指针和它指向的东西是两个不同的东西。在内存中的位置无关紧要,指针可以随心所欲地递增。在这种情况下,工作使用通常的数字,它是一个指针:
按照标准,一切都很好。它不会导致任何 UB。是的,这一切都在标准中定义。
为了更好地演示,我们可以采用更简单的类型:
在这种情况下,我们得到数组的维度,它将等于一。如果您需要获取类型的维度,则可以执行以下操作:
但最好不要重新发明轮子并使用 sizeof。