Доступ к элементам с помощью std::vector::data()

В этом фрагменте кода я использую метод data() вектора для доступа к его элементам:

#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector (5);

  int* p = myvector.data();

  *p = 10;
  ++p;
  *p = 20;
  p[2] = 100;

  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';

  return 0;
}

Который генерирует

myvector contains: 10 20 0 100 0

Вопрос в том, почему p[2] равен 0? Если предположить, что myvector.data() — это указатель на первый элемент (индекс 0), то p[0] — это 10, что нормально. ++p указывает на следующий элемент, который равен p[1] и равен 20, что тоже нормально. Теперь p[2] — следующий элемент после p[1]. Итак, последовательность должна быть 10 20 100. В чем здесь ошибка?

"++p points to the next element ...", а также увеличивает p себя.
G.M. 23.02.2024 12:08

Вы перепутали myvector[2] и p[2]? Последний индексируется относительно самого p.

HolyBlackCat 23.02.2024 12:09

Попробуйте тот же код, но с массивом и указателем, и вы получите точно такой же результат.

john 23.02.2024 12:11
p изначально был равен &myvector[0], но после приращения эквивалентен &myvector[1]. Таким образом, p[2] эквивалентно myvector[3]. Поскольку все элементы myvector были инициализированы нулями, а myvector[2] не изменилось, оно по-прежнему равно нулю.
Peter 23.02.2024 12:11

Да, я перепутал это с моим вектором. Спасибо.

mahmood 23.02.2024 12:11

Также обратите внимание, что C++ не рекомендует вам выполнять (сложную) арифметику необработанных указателей. Метод данных обычно используется только для взаимодействия с устаревшими API, которым по-прежнему требуется необработанный указатель. В большинстве других кодов следует использовать итераторы или циклы for на основе диапазона.

Pepijn Kramer 23.02.2024 15:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
117
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы увеличили p на 1, а затем записали в p[2], что фактически теперь равно myvector[3].

Написание p[1] приведет к записи в поле, соседнем с *p.

Вы инициализируете указатель p, чтобы он указывал на myvector[0].

Затем вы увеличиваете p, и теперь оно указывает на myvector[1].

Если нарисовать, то это примерно так:

+-------------+-------------+-------------+-------------+-------------+
| myvector[0] | myvector[1] | myvector[2] | myvector[3] | myvector[4] |
+-------------+-------------+-------------+-------------+-------------+
                ^
                |
                p

Теперь важная часть: для любого указателя или массива p и индекса i выражение p[i] в точности равно *(p + i).

Это означает, что p[2] в вашем коде совпадает с *(p + 2). А поскольку p указывает на второй элемент вектора, p + 2 будет указывать на четвертый.

Еще раз нарисуем:

+-------------+-------------+-------------+-------------+-------------+
| myvector[0] | myvector[1] | myvector[2] | myvector[3] | myvector[4] |
+-------------+-------------+-------------+-------------+-------------+
                ^             ^             ^             ^
                |             |             |             |
                p             p + 1         p + 2         p + 3

Итак, когда вы пишете на p[2], это то же самое, что писать на myvector[3].

Согласно стандарту C++17 (подписка 8.2.1)

1 Постфиксное выражение, за которым следует выражение в квадратных скобках, постфиксное выражение. Одним из выражений должно быть значение glvalue введите «массив T» или значение типа «указатель на T», а другое должно быть значением перечисления без области действия или целочисленного типа. результат имеет тип «Т». Тип «Т» должен быть полностью определенным тип объекта. Выражение E1[E2] идентично (по определению) *((E1)+(E2)) [ Примечание: подробности о * и + см. в 8.3 и 8.7, а подробности о массивах — в 11.3.4. — конечное примечание] , за исключением того, что в случае операнда-массива результатом является lvalue, если этот операнд является lvalue и xvalue в противном случае. Выражение E1 секвенируется перед выражение Е2.

Итак, поскольку указатель p сначала указывает на первый элемент вектора, а затем после увеличения указателя p

++p;
*p = 20;

теперь он указывает на второй элемент вектора. И, наконец, выражение p[2] в этом заявлении

p[2] = 100;

что эквивалентно выражению *( p + 2) дает четвертый элемент вектора.

Если перед этим оператором ввести промежуточный указатель

++p;

нравиться

int *q = p'

тогда выражение p[2], оцененное как *( p + 2 ), эквивалентно *( ( q + 1 ) + 2 ), то же самое, что *( q + 3 ). Таким образом, четвертый элемент вектора обновляется.

Давайте реорганизуем ваш код, чтобы p всегда указывал на первый элемент:

*p = 10;
//++p;  // removed this line and replaced p with p+1 below
*(p+1) = 20;
(p+1)[2] = 100;

Далее, учтите, что a[b] для встроенного [] в точности эквивалентно *(a+b), следовательно, последняя строка *(p+1+2) is *(p+3) is p[3].

После того, как вы увеличили p один раз, индекс подписки будет соответствовать обновленному указателю. x[2] добавляет 2 к x, а затем разыменовывает его. Обычно x указывает на первый элемент массива, а ваш — на второй.

Другие вопросы по теме