Как * (& arr + 1) - arr дает длину в элементах массива arr? - PullRequest
4 голосов
/ 15 апреля 2020
#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

Может кто-нибудь объяснить? Потому что, когда я запускаю следующие два кода, я получаю один и тот же вывод для следующего:

&arr (я думаю, что это указывает на адрес первого элемента arr)

*(&arr) тогда я не совсем понимаю, что это делает, что символ * делает с &arr (то есть с адресом здесь) ?, потому что два выхода одинаковы, когда я их запускаю

и, наконец, что именно происходит, когда целое число 1 добавляется к адресу с помощью следующего кода: &arr + 1

Ответы [ 3 ]

3 голосов
/ 15 апреля 2020

Это мое поле, но я попробую:

  • &arr возвращает указатель на int[5]
  • + 1 шаг указателя one 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 или не открыт, но ссылается на объект до того, как его хранилище было выделено делает выглядит немного подозрительно.

1 голос
/ 16 апреля 2020

Учитывая следующие факты:

  • Когда вы увеличиваете / уменьшаете указатель на целое значение X, значение указателя увеличивается / уменьшается в X раз количество байтов типа, на который указывает указатель.

  • Когда вы вычитаете 2 указателя одного и того же типа, результатом является разница между их удерживаемыми адресами, разделенная на количество байтов типа, на который указывают.

  • Когда вы ссылаетесь на массив только по его имени, он распадается в указатель на 1-й элемент массива.

Тип вашей переменной arr: int[5], ie - массив из 5 int с. &arr возвращает указатель int[5]* на arr (технически он на самом деле написан как int(*)[5], но не будем беспокоиться об этом здесь, для простоты). Позволяет назвать этот указатель temp ниже.

Затем + 1 увеличивает значение temp на 1 int[5] элемент. Другими словами, адрес, сохраненный в temp, увеличивается на 1 * sizeof(int[5]), или 1 * (sizeof(int) * 5), число байтов. Это фактически дает вам указатель int[5]* до конца arr (ie, до &arr[5]). Элемент int[5] физически не существует по этому адресу памяти, но создание указателя на него является законным для целей арифметики указателей c.

Разыменование temp дает вам ссылка на int[5] в конце arr. Эта ссылка распадается в указатель int* при передаче в operator-.

В - arr, ссылка на arr распадается в int* указатель на arr[0] при передаче на operator-.

Таким образом, с учетом этого кода:

int len = *(&arr + 1) - arr;

Что фактически совпадает с этим:

int len = &arr[5] - &arr[0];

Что фактически совпадает с этим:

int len = (<address of arr[5]> - <address of arr[0]>) / sizeof(int);

Таким образом, результат равен 5.

1 голос
/ 16 апреля 2020

Пример:

int  arr[] = {1, 2, 3, 4, 5, 6}; 
int size = *(&arr + 1) - arr; 

Здесь арифметика указателя c выполняет свою роль. Нам не нужно явно преобразовывать каждое из мест в символьные указатели.

&arr ==> Указатель в массив из 6 элементов. [См. Это для различия между & arr и arr]

(&arr + 1) ==> Адрес на 6 целых чисел впереди, так как тип указателя является указателем на массив из 6 целых чисел.

*(&arr + 1) ==> Тот же адрес, что и (& arr + 1), но тип указателя "int *".

*(&arr + 1) - arr ==> Поскольку * (& arr + 1) указывает на адрес на 6 целых чисел перед arr, разница между двумя - 6.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...