it-roy-ru.com

Инициализация нормального массива с одним значением по умолчанию

C++ Notes: инициализация массива имеет список Nice для инициализации массивов. у меня есть

int array[100] = {-1};

ожидая, что он будет заполнен -1, но его нет, только первое значение равно нулю, а остальные 0 смешаны со случайными значениями.

Код

int array[100] = {0};

работает просто отлично и устанавливает каждый элемент на 0.

Чего мне здесь не хватает .. Разве нельзя инициализировать его, если значение не равно нулю?

2: Происходит ли инициализация по умолчанию (как указано выше) быстрее, чем обычный цикл по всему массиву и присваивается значение, или она делает то же самое?

222
Milan

Используя синтаксис, который вы использовали,

int array[100] = {-1};

говорит "установите первый элемент на -1, а остальные на 0", поскольку все пропущенные элементы установлены на 0.

В C++, чтобы установить их все в -1, вы можете использовать что-то вроде std::fill_n (из <algorithm>):

std::fill_n(array, 100, -1);

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

322
Evan Teran

Существует расширение для компилятора gcc, которое допускает синтаксис:

int array[100] = { [0 ... 99] = -1 };

Это установит все элементы в -1.

Это известно как "Назначенные инициализаторы", см. здесь для получения дополнительной информации.

Обратите внимание, что это не реализовано для компилятора gcc c ++.

127
Callum

Страница, на которую вы ссылались, уже дала ответ на первую часть:

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

Не существует встроенного способа инициализации всего массива каким-либо ненулевым значением.

Что касается того, что быстрее, то применяется обычное правило: "Метод, который дает компилятору наибольшую свободу, вероятно, быстрее".

int array[100] = {0};

просто говорит компилятору "установить эти 100 ints в ноль", которые компилятор может свободно оптимизировать.

for (int i = 0; i < 100; ++i){
  array[i] = 0;
}

намного конкретнее. Он говорит компилятору создать переменную итерации i, он сообщает ему порядок, в котором элементы должны быть инициализированы, и так далее. Конечно, компилятор, скорее всего, оптимизирует это, но дело в том, что здесь вы чрезмерно уточняете проблему, заставляя компилятор работать усерднее, чтобы получить тот же результат.

Наконец, если вы хотите установить для массива ненулевое значение, вы должны (по крайней мере, в C++) использовать std::fill:

std::fill(array, array+100, 42); // sets every value in the array to 42

Опять же, вы можете сделать то же самое с массивом, но это более кратко и дает компилятору больше свободы. Вы просто говорите, что хотите, чтобы весь массив был заполнен значением 42. Вы ничего не говорите о том, в каком порядке это должно быть сделано, или о чем-то еще.

31
jalf

C++ 11 имеет еще одну (несовершенную) опцию:

std::array<int, 100> a;
a.fill(-1);
10
Timmmm

С помощью {} вы назначаете элементы так, как они объявлены; остальное инициализируется 0.

Если нет = {} для инициализации, содержимое не определено.

9
0x6adb015

На странице, на которую вы ссылались, указано

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

Проблема скорости: любые различия будут незначительными для таких маленьких массивов. Если вы работаете с большими массивами, а скорость гораздо важнее размера, вы можете получить массив const со значениями по умолчанию (инициализированный во время компиляции), а затем memcpy их в модифицируемый массив.

8
laalto

Другим способом инициализации массива с общим значением будет создание списка элементов в последовательности определений:

#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )

#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )

Инициализация массива к общему значению может быть легко выполнена:

#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };

Примечание: DUPx введен для включения подстановки макросов в параметрах в DUP

4
Steen

Используя std::array, мы можем сделать это довольно простым способом в C++ 14. Это возможно сделать только в C++ 11, но немного сложнее.

Наш интерфейс имеет размер во время компиляции и значение по умолчанию.

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}


template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}

Третья функция предназначена в основном для удобства, поэтому пользователю не нужно самим создавать std::integral_constant<std::size_t, size>, так как это довольно многословная конструкция. Настоящая работа выполняется одной из первых двух функций.

Первая перегрузка довольно проста: она создает std::array размера 0. Копирование не требуется, мы просто создаем его.

Вторая перегрузка немного сложнее. Он пересылает значение, полученное в качестве источника, а также создает экземпляр make_index_sequence и просто вызывает некоторую другую функцию реализации. Как выглядит эта функция?

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail

Это создает первый аргумент размера - 1 путем копирования значения, которое мы передали. Здесь мы используем наши индексы пакета переменных параметров просто как нечто для расширения. В этом пакете есть записи размера - 1 (как мы указали в конструкции make_index_sequence), и они имеют значения 0, 1, 2, 3, ..., size - 2. Однако нас не волнуют значения (поэтому мы приводим его к void, чтобы заглушить любые предупреждения компилятора). Расширение пакета параметров расширяет наш код до чего-то вроде этого (предполагая размер == 4):

return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };

Мы используем эти скобки, чтобы гарантировать, что расширение пакета переменных ... расширяет то, что мы хотим, а также чтобы убедиться, что мы используем оператор запятой. Без скобок было бы похоже, что мы передаем кучу аргументов в нашу инициализацию массива, но на самом деле мы оцениваем индекс, приводим его к void, игнорируем этот результат void, а затем возвращаем значение, которое копируется в массив ,.

Последний аргумент, который мы называем std::forward, - это небольшая оптимизация. Если кто-то передает временную строку std :: string и говорит "создайте массив из 5 из них", мы бы хотели иметь 4 копии и 1 ход вместо 5 копий. std::forward гарантирует, что мы делаем это.

Полный код, включая заголовки и некоторые модульные тесты:

#include <array>
#include <type_traits>
#include <utility>

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}

template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}



struct non_copyable {
    constexpr non_copyable() = default;
    constexpr non_copyable(non_copyable const &) = delete;
    constexpr non_copyable(non_copyable &&) = default;
};

int main() {
    constexpr auto array_n = make_array_n<6>(5);
    static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
    static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
    static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");

    constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
    static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");

    constexpr auto array_empty = make_array_n<0>(2);
    static_assert(array_empty.empty(), "Incorrect array size for empty array.");

    constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
    static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
3
David Stone

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

Есть пример здесь .

2
Steve Melnikoff

В C++ также возможно использование метапрограммирования и шаблонов с переменными значениями. В следующем посте показано, как это сделать: Программно создавать статические массивы во время компиляции в C++ .

1
ingomueller.net

1) Когда вы используете инициализатор, для структуры или подобного массива, неопределенные значения создаются по умолчанию. В случае примитивного типа, такого как int, это означает, что они будут обнулены. Обратите внимание, что это применяется рекурсивно: у вас может быть массив структур, содержащих массивы, и если вы укажете только первое поле первой структуры, то все остальные будут инициализированы нулями и конструкторами по умолчанию.

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

1
Boojum

В языке программирования C++ V4 Страуструп рекомендует использовать векторы или переменные над встроенными массивами. С valarrary, когда вы создаете их, вы можете инициировать их к определенному значению, например:

valarray <int>seven7s=(7777777,7);

Инициализировать массив длиной 7 элементов с "7777777".

Это способ реализации ответа на языке C++ с использованием структуры данных C++ вместо "простого старого массива C".

Я переключился на использование valarray как попытку в своем коде попытаться использовать C++ 'isms v. C'isms ....

0
Astara