Вычислить длину массива с помощью арифметики указателя - PullRequest
0 голосов
/ 09 октября 2018

Мне было интересно, как на самом деле работает *(&array + 1).Я видел это как простой способ вычислить длину массива и хочу понять это правильно перед использованием.Я не очень разбираюсь в арифметике указателей, но, насколько я понимаю, &array дает адрес первого элемента массива.(&array + 1) будет идти в конец массива с точки зрения адреса.Но *(&array + 1) не должно давать значение, которое находится по этому адресу.Вместо этого он печатает адрес.Я был бы очень признателен за вашу помощь, чтобы у меня в голове все прояснилось.

Вот простой пример, над которым я работаю:

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

Ответы [ 3 ]

0 голосов
/ 09 октября 2018

Выражение &numbers дает вам адрес массива , а не первого члена (хотя численно они совпадают).Тип этого выражения int (*)[7], то есть указатель на массив размером 7.

Выражение &numbers + 1 добавляет sizeof(int[7]) байтов к адресу array.Результирующий указатель указывает сразу после массива.

Проблема, однако, заключается в том, когда вы затем разыменовываете этот указатель с помощью *(&numbers + 1).Разыменование указателя, который указывает на один элемент за концом массива, вызывает неопределенное поведение .

. Правильный способ получить количество элементов массива - sizeof(numbers)/sizeof(numbers[0]).Это предполагает, что массив был определен в текущей области и не является параметром для функции.

0 голосов
/ 09 октября 2018

но с моим пониманием & массив дает адрес первого элемента массива.

Это понимание вводит в заблуждение.&array дает адрес массива.Конечно, значение этого адреса совпадает с первым элементом, но тип выражения отличается.Тип выражения &array - это «указатель на массив из N элементов типа T» (где N - искомая длина, а T - int).

Но*(&array + 1) не должен давать значение, которое находится по этому адресу.

Хорошо, да ... но именно здесь тип выражения становится важным.Обращение указателя на массив (а не указатель на элемент массива) приведет к созданию самого массива.

В выражении вычитания оба операнда массива переходят в указатель на первый элемент.Поскольку в вычитании используются затухающие указатели, единица арифметики указателей определяется размером элемента.

Я видел это как простой способ вычисления длины массива

* 1022.* Есть более простые способы:
std::size(numbers)

А в С:

sizeof(numbers)/sizeof(numbers[0])
0 голосов
/ 09 октября 2018

(Этот ответ для C ++.)

  1. &numbers - указатель на сам массив.Он имеет тип int (*)[7].
  2. &numbers + 1 - указатель на байт сразу после массива, где будет расположен другой массив из 7 int s.Он по-прежнему имеет тип int (*)[7].
  3. *(&numbers + 1) разыменовывает этот указатель, получая lvalue типа int[7] со ссылкой на байт сразу после массива.
  4. *(&numbers + 1) - numbers: ИспользованиеОператор - заставляет оба операнда подвергаться преобразованию массива в указатель, поэтому указатели могут быть вычтены.*(&numbers + 1) преобразуется в int*, указывающий на байт после массива.numbers преобразуется в int*, указывающий на первый байт массива.Разница между ними заключается в количестве int с между двумя указателями, т. Е. Количестве int с в массиве.

Редактировать: Хотя на действительный объект, на который указывает *, нет1028 *, это то, что называется указателем «мимо конца».Если p является указателем на T, указывающим на действительный объект типа T, тогда всегда допустимо вычислять p + 1, даже если *p может быть единственным объектом или объектом в концемассива.В этом случае вы получаете указатель «после конца», который не указывает на действительный объект, но все еще является действительным указателем.Вы можете использовать этот указатель для арифметики указателя и даже разыменовать его, чтобы получить lvalue, если вы не пытаетесь читать или записывать это lvalue.Обратите внимание, что вы можете пройти только один байт за концом объекта;попытка пойти дальше ведет к неопределенному поведению.

...