[英]How does *(&arr + 1) - arr give the length in elements of array arr?
#include <iostream>
using namespace std;
int main() {
int arr[5] = {5, 8, 1, 3, 6};
int len = *(&arr + 1) - arr;
cout << "The length of the array is: " << len;
return 0;
}
对于上面的代码,我不太明白这两段代码在做什么:
*(&arr + 1)
和
*(&arr)
&arr
有人可以解释一下吗? 因为当我运行以下两个代码时,我得到相同的 output 如下:
&arr
(我觉得这个指向arr的第一个元素的地址)
*(&arr)
那么我不太明白这是做什么的,符号*
对&arr
做了什么(即到这里的地址)?,因为当我运行它们时,这两个输出是相同的
最后,当 integer 说 1 通过此代码添加到地址时,到底发生了什么: &arr + 1
这是一个雷区,但我会试一试:
&arr
返回一个指向int[5]
的指针+ 1
步指针 1 int[5]
*(&arr + 1)
将结果解引用回int(&)[5]
*(&arr + 1) - arr
在两个int[5]
衰减为int
指针后执行指针运算,返回两个int
指针之间的差异,即5
。重写以使其更清晰:
int arr[5] = {5, 8, 1, 3, 6};
int (*begin_ptr)[5] = &arr + 0; // begin_ptr is a int(*)[5]
int (*end_ptr)[5] = &arr + 1; // end_ptr is a int(*)[5]
// Note:
// begin_ptr + 1 == end_ptr
// end_ptr - begin_ptr == 1
int (&begin_ref)[5] = *begin_ptr; // begin_ref is a int(&)[5]
int (&end_ref)[5] = *end_ptr; // end_ref is a int(&)[5] UB here?
auto len = end_ref - begin_ref; // the array references decay into int*
std::cout << "The length of the array is: " << len << '\n'; // 5
如果它是 UB 或未打开,我会留下这个问题,但在分配引用的存储之前引用 object确实看起来有点可疑。
例子:
int arr[] = {1, 2, 3, 4, 5, 6};
int size = *(&arr + 1) - arr;
在这里,指针算术发挥了作用。 我们不需要将每个位置显式转换为字符指针。
&arr
==> 指向包含 6 个元素的数组的指针。 [查看 &arr 和 arr 之间的区别]
(&arr + 1)
==> 前面 6 个整数的地址,因为指针类型是指向 6 个整数数组的指针。
*(&arr + 1)
==> 与 (&arr + 1) 相同的地址,但指针类型为“int *”。
*(&arr + 1) - arr
==> 由于 *(&arr + 1) 指向 arr 前面 6 个整数的地址,所以两者之差为 6。
鉴于以下事实:
当您将指针增加/减少整数值X
时,指针的值将增加/减少X
乘以指针指向的类型的字节数。
当您减去 2 个相同类型的指针时,结果是它们持有的地址之间的差异,除以所指向的类型的字节数。
当您仅通过名称引用数组时,它会衰减为指向数组第一个元素的指针。
您的arr
变量的类型是int[5]
,即 5 个int
的数组。 &arr
返回一个指向arr
的int[5]*
指针(从技术上讲,它实际上写成int(*)[5]
,但为了简单起见,这里不用担心)。 让我们在下面调用这个指针temp
。
然后, + 1
将temp
的值增加 1 个int[5]
元素。 换句话说,存储在temp
中的地址增加了1 * sizeof(int[5])
或1 * (sizeof(int) * 5)
字节数。 这有效地为您提供了一个指向arr
结尾的int[5]*
指针(即指向&arr[5]
)。 在该 memory 地址处物理上不存在int[5]
元素,但出于指针运算的目的,创建指向它的指针是合法的。
取消引用temp
为您提供了对arr
末尾的int[5]
的引用。 该引用在传递给operator-
时衰减为int*
指针。
在- arr
中,对arr
的引用在传递给operator-
时衰减为指向arr[0]
的int*
指针。
因此,给定以下代码:
int len = *(&arr + 1) - arr;
这实际上与此相同:
int len = &arr[5] - &arr[0];
这实际上与此相同:
int len = (<address of arr[5]> - <address of arr[0]>) / sizeof(int);
因此,结果为 5。
也许我加入讨论为时已晚,但我认为这是一个很好的问题,值得更彻底的回答。
我最初在这里看到 op 片段
这里总共有 4 个操作。
&arr
实际上做的是动态创建一个二维数组,它的第一个维度等于 1,并获取指向这个二维数组头部的指针。 如果你不熟悉二维数组,Shahbaz 在Why can't we can't double pointer to representation two dimension arrays?
特别是post中array2
的结构体,指向这个新创建的二维数组的指针类型为int (*)[5]
&arr+1
中的+1
对其第一维进行指针运算。 回想一下,第一个维度只是 1。这正是(&arr + 1)
指向原始数组末尾之后的 memory 地址的原因。
*
*(&arr + 1)
中的 * 将二维数组指针(类型为int (*)[5]
)转换回一维数组指针(类型为int*
)。
最后, *(&arr + 1) - arr
- arr
指针减法。 根据标准( N1570
):
6.5.6 加法运算符
……
9当两个指针相减时,都应指向同一个数组 object 的元素,或数组 object 的最后一个元素的后一个; 结果是两个数组元素的下标之差。
最后一个问题出现了,在讨论如何确定 C 中数组的大小? 我们知道sizeof
方法只适用于堆栈上的 arrays,但是这个方法怎么样? 不幸的是,此方法也仅适用于堆栈。 如果收到 function 中的数组指针,则数组大小信息丢失,您无法为其动态创建二维数组。 接下来的指针算术将简单地分崩离析。
&arr ==> 指向包含 n 个元素的数组的指针。 (&arr + 1) ==> 前面 6 个整数的地址,因为指针类型是指向 n 个整数数组的指针。
*(&arr + 1) ==> 与 (&arr + 1) 相同的地址,但指针类型为“int *”。
*(&arr + 1) - arr ==> 由于 *(&arr + 1) 指向 arr 前面 n 个整数的地址,所以两者之差为 n。
更多关于这个...>>>>>>>>>>>
诀窍是使用表达式 (&arr)[1] - arr 来获取数组 arr 的大小。 arr 和 &arr 都指向同一个 memory 位置,但它们都有不同的类型。
arr 的类型为 int* 并衰减为指向数组第一个元素的指针。 因此,关于数组大小的任何知识都消失了。
&arr 产生一个 int (*)[n] 类型的指针,即指向 n 个 int 数组的指针。 因此,&arr 指向整个数组,而 *(&arr + 1) (或 &arr)[1]) 指向数组之后的下一个字节。
这是因为指针算法在 C 中的工作方式。 我们知道,指向 int 的指针在递增 1 时会前移 sizeof(int)。类似地,指向 int[n] 的指针会前移 sizeof(int[n]),即整个数组的大小。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.