Диапазон для цикла для распределенных массивов кучи

Рассмотрим следующий код, который будет cout success ровно три раза:

int arr [3];

for(int& value : arr )
    std::cout << "success" << std::endl;

Если я пытаюсь разместить массив в куче, возникают проблемы. Этот код не компилируется:

int* ptr = new int[3];

for(int& value : *ptr )
    std::cout << "success" << std::endl;

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

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

2 ответа

Решение

Необработанный массив поддерживает синтаксис, основанный на диапазоне, только если видимое объявление включает в себя количество элементов, т.е. int arr[3] в первом случае и int* prt во втором случае. В первом случае это дается (но все же вы должны предпочесть std::array если возможно), но не во втором случае вы выделяете память в куче и информация о размере исчезает. Вы можете обойти это, если вы просто используете std::array а не необработанный массив.

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

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

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

Могу ли я заставить его работать с небольшим изменением

Да, ты можешь. использование std::array или же st::vector который будет выглядеть так для std::array:

#include <iostream>
#include <array>

int main()
{
    std::array<int, 3>* ptr = new std::array<int, 3>;

    for(int& value : *ptr )
        std::cout << "success" << std::endl;
}

Для краткости я не включил удаление для указателя, которое вы всегда должны делать.

Однако, если вы выделяете память в куче, почти всегда лучше использовать std::vector как освобождение будет заботиться автоматически. В программе, чем читать:

#include <iostream>
#include <vecor>

int main()
{
    std::vector<int> vec(3);

    for(int& value : vec)
        std::cout << "success" << std::endl;
}

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

Присмотритесь к типу указателя: int* ptr, Это указатель на int Сравните это с типом arr который int[3], Эти типы разные. Итак, ваше предположение, что типы должны быть одинаковыми, неверно. int[3] это диапазон, в то время как int не является.

Массив-новое-выражение возвращает указатель на первый элемент массива. Что это ptr указывает на. Адрес памяти первого элемента такой же, как и у всего массива, но тип переменной определяет способ ее использования.

Могу ли я заставить его работать с небольшим изменением?

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

auto arr_ptr = std::launder(reinterpret_cast<int (*)[3]>(ptr));
for(int& value : *arr_ptr)

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

Кроме того, ваш пример кода пропускает массив. Хотя утечку легко исправить в этом коде, ручное управление памятью, как правило, сложно и не нужно. Вы наверняка лучше использовать std::vector когда вам нужен динамический массив:

std::vector<int> v(3);
for(int& value : v)
Другие вопросы по тегам