繁体   English   中英

*(&arr + 1) - arr 如何给出数组 arr 元素的长度?

[英]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返回一个指向arrint[5]*指针(从技术上讲,它实际上写成int(*)[5] ,但为了简单起见,这里不用担心)。 让我们在下面调用这个指针temp

然后, + 1temp的值增加 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。

  1. (&arr + 1) 指向数组末尾之后的 memory 地址。
  2. *(&arr + 1) 只是将上述地址转换为 int *。
  3. 从数组末尾的地址中减去数组的开头地址,得到数组的长度。

更多关于这个...>>>>>>>>>>>

诀窍是使用表达式 (&arr)[1] - arr 来获取数组 arr 的大小。 arr 和 &arr 都指向同一个 memory 位置,但它们都有不同的类型。

  1. arr 的类型为 int* 并衰减为指向数组第一个元素的指针。 因此,关于数组大小的任何知识都消失了。

  2. &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.

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