Перейти к содержимому

C++ массивы (std::array)

  • Преимущество C++ массивов над C массивами
  • Копирование и инициализация массивов
  • Индексирование C++ массивов
  • data
  • Получение ссылок на элементы массива
  • Получение адресов элементов массива
  • Типы данных кроме int под элементы массива
std::array<int, 2> arr{ 1, 2 };
int* pa = a.data();
int& b = *pa;
pa += 1;
std::cout << b << std::endl;

Что такое data?

a.data() дает адрес первого элемента из массива (&a[0], a[0] дает ссылку, потом берем адрес из ссылки).

Ответ

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

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2> other{ 3, 4 };
arr = other;
Ответ

Тут каждый элемент копириуется из other в arr.

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2> other{ arr };
Ответ

Тут тоже, но при создании other.

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2>& other{ arr };
other[0] = 3;
std::cout << arr[0] << std::endl;
Ответ

Здесь иллюстрируется создание ссылки на объект типа С++ массива. other как бы заменяете на arr.

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2>* p{ &arr };
*p = { 3, 4 };
std::cout << arr[0] << std::endl;
std::cout << arr[1] << std::endl;
Ответ

p будет содержать адрес начала всего объекта массива. Объект массива покрывает все его элементы.

Здесь, изначальные элементы перезаписываются на 3, 4.

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2>* p{ &arr };
std::array<int, 2> arr1{ *p };
arr1[0] = 3;
arr1[1] = 4;
std::cout << arr[0] << std::endl;
std::cout << arr[1] << std::endl;
Ответ

Тип у p это std::array<int, 2>, выражение *p даст значение типа std::array<int, 2>, то есть в arr1 попадет копия всего массива

Поскольку arr1 это копия, ее изменения не повлияют на arr.

std::array<int, 2> arr{ 1, 2 };
int& a = arr[0];
a = 3;
Ответ

a = 3 перезаписывает первый элемент массива, поскольку в a была сохранена ссылка на 1 элемент (на объект внутри массива).

std::array<int, 2> arr{ 1, 2 };
std::array<int, 2>* p{ &arr };
(*p)[0] = 3;
(*p)[1] = 4;
std::cout << arr[0] << std::endl;
std::cout << arr[1] << std::endl;
Ответ

Здесь, *p — это выражение типа std::array<int, 2>&, то есть дает ссылку на массив, на память переменной arr (потому что в p адрес arr).

Индексирование относительно (*p) будет давать ссылки на элементы в массиве. Например, тип выражения (*p)[0] — это int&, а ссылаться это будет на первый элемент в массиве, в переменной arr.

Перезапись также будет применяться к элементам arr.

std::array<int, 2> arr { 1, 2 };
int& arr1 = *(p.data() + 1)
Ответ

Аналогично коду выше, только выражение сложнее. Тут в arr1 попадает адрес arr[1].

10. Взаиморасположение базового адреса и самого объекта массива

Заголовок раздела «10. Взаиморасположение базового адреса и самого объекта массива»
std::array<int, 2> arr { 1, 2 };
ptrdiff_t diff { reinterpret_cast<uint8_t*>(arr.data()) - reinterpret_cast<uint8_t*>(&arr) };
std::cout << diff << std::endl;
Ответ

Начало всего массива находится там же, где и первый элемент массива. Массив std::array это и есть его элементы.

Выведется 0.

std::array<std::array<int, 2>, 3> arr{
std::array{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
};
std::array<int, 2> firstRow { arr[0] };
std::array<int, 2> lastRow { arr[2] };
arr[0][0] = 7;
arr[2][1] = 8;
arr[1] = { 9, 10 };
Ответ

Тут иллюстрируется использование массива, где тип элемента не int, а массив. Каждый элемент массива по сути теперь хранит в себе по два инта.

В инициализации arr необходимо указать std::array, потому что иначе компилятор не может определить тип элемента из инициализации. Ему нужно слегка помочь.

Механизм этого вам пока не обязательно понимать.

Можно ли использовать пользовательский тип как элемент массива?

Можно. Попробуйте определить тип, например:

struct Item
{
int f1;
int f2;
};

И использовать его в качестве типа элемента, вот так:

std::array<Item, 3> arr{
Item{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
};