通过指针算法计算数组长度

Calculate array length via pointer arithmetic

我想知道 *(&array + 1) 实际上是如何工作的。我认为这是计算数组长度的一种简单方法,并希望在使用它之前正确理解它。我对指针运算不是很有经验,但根据我的理解 &array 给出了数组第一个元素的地址。 (&array + 1) 将根据地址到达数组的末尾。但是 *(&array + 1) 不应该给出这个地址的值。相反,它打印出地址。非常感谢你的帮助,让我头脑中的指针内容变得清晰。

这是我正在处理的简单示例:

int numbers[] = {5,8,9,3,4,6,1};
int length = *(&numbers + 1) - numbers;

(此答案适用于 C++。)

  1. &numbers 是指向数组本身的指针。它的类型为 int (*)[7].
  2. &numbers + 1 是指向数组后面字节的指针,另一个 7 int 数组将位于该字节。它的类型仍然是 int (*)[7].
  3. *(&numbers + 1) 取消引用这个指针,产生一个 int[7] 类型的左值,引用数组后面的字节。
  4. *(&numbers + 1) - numbers:使用-运算符强制两个操作数进行数组到指针的转换,因此可以减去指针。 *(&numbers + 1) 转换为 int* 指向数组后的字节。 numbers 转换为指向数组第一个字节的 int*。它们的区别在于两个指针之间int的个数---也就是数组中int的个数。

编辑:虽然 &numbers + 1 没有指向有效的对象,这就是所谓的 "past the end" 指针。如果 p 是指向 T 的指针,指向类型 T 的有效对象,那么计算 p + 1 总是有效的,即使 *p 可能是单个对象,或数组末尾的对象。在那种情况下,您会得到一个 "past the end" 指针,它不指向有效对象,但仍然是一个有效指针。您可以将此指针用于指针运算,甚至取消引用它以生成左值,只要您不尝试读取或写入该左值即可。请注意,您只能在对象末尾后移动一个字节;试图走得更远会导致未定义的行为。

表达式 &numbers 为您提供 数组 的地址,而不是第一个成员(尽管在数字上它们是相同的)。此表达式的类型是 int (*)[7],即指向大小为 7 的数组的指针。

表达式&numbers + 1sizeof(int[7]) 个字节添加到array 的地址。结果指针指向数组之后。

然而,问题是当您随后使用 *(&numbers + 1) 取消引用此指针时。取消引用指向一个元素超过数组末尾的指针会调用 undefined behavior.

获取数组元素数量的正确方法是sizeof(numbers)/sizeof(numbers[0])。这假定数组是在当前范围内定义的,而不是函数的参数。

but with my understanding &array gives the address of the first element of the array.

这种理解具有误导性。 &array给出数组的地址。当然,该地址的值与第一个元素相同,但表达式的类型不同。表达式 &array 的类型是 "pointer to array of N elements of type T"(其中 N 是您要查找的长度,T 是 int)。

But shouldn't *(&array + 1) give the value, which is at this address.

嗯,是的...但是表达式的类型在这里变得很重要。间接指向数组的指针(而不是指向数组元素的指针)将导致数组本身。

在减法表达式中,两个数组操作数都衰减为指向第一个元素的指针。由于减法使用了衰减指针,因此指针运算的单位是元素大小。

I saw this as an easy way to calculate the array length

还有更简单的方法:

std::size(numbers)

在 C 中:

sizeof(numbers)/sizeof(numbers[0])