Инициализация обычного массива с одним значением по умолчанию
С++ Notes: Инициализация массива имеет хороший список для инициализации массивов. У меня есть
int array[100] = {-1};
ожидая, что он будет заполнен с -1, но его нет, будет только первое значение, а остальные 0 смешаны со случайными значениями.
Код
int array[100] = {0};
отлично работает и устанавливает каждый элемент в 0.
Что мне здесь не хватает.. Не удается инициализировать его, если значение не равно нулю?
2: Является ли инициализация по умолчанию (как указано выше) более быстрой, чем обычный цикл через весь массив, и присваивает значение или делает то же самое?
Ответы
Ответ 1
Используя синтаксис, который вы использовали,
int array[100] = {-1};
говорит: "Установите первый элемент в -1
, а остальное - на 0
", поскольку все пропущенные элементы установлены на 0
.
В С++, чтобы установить их все в -1
, вы можете использовать что-то вроде std::fill_n
(от <algorithm>
):
std::fill_n(array, 100, -1);
В портативном C вам нужно перевернуть свой собственный цикл. Существуют расширения-компиляторы, или вы можете зависеть от поведения, определенного реализацией, как ярлыка, если это приемлемо.
Ответ 2
Существует расширение для gcc-компилятора, который позволяет использовать синтаксис:
int array[100] = { [0 ... 99] = -1 };
Это установило бы все элементы в -1.
Это называется "назначенные инициализаторы", см. здесь для получения дополнительной информации.
Обратите внимание, что это не реализовано для компилятора gcc С++.
Ответ 3
Страница, с которой вы связались, уже ответила на первую часть:
Если размер явного массива равен указанный, но более короткий список инициализации указан, неуказанные элементы установлены на ноль.
Нет встроенного способа инициализации всего массива некоторым ненулевым значением.
Как для этого быстрее, применяется обычное правило: "Метод, который дает компилятору большую свободу, скорее всего быстрее".
int array[100] = {0};
просто сообщает компилятору "установить эти 100 ints на ноль", которые компилятор может оптимизировать свободно.
for (int i = 0; i < 100; ++i){
array[i] = 0;
}
является более конкретным. Он сообщает компилятору создать переменную итерации i
, она сообщает ей порядок, в котором элементы должны быть инициализированы, и так далее. Конечно, компилятор, скорее всего, оптимизирует это, но дело в том, что здесь вы слишком сильно определяете эту проблему, заставляя компилятор работать усерднее, чтобы получить тот же результат.
Наконец, если вы хотите установить массив на ненулевое значение, вы должны (по крайней мере, на С++) использовать std::fill
:
std::fill(array, array+100, 42); // sets every value in the array to 42
Опять же, вы можете сделать то же самое с массивом, но это более кратким и дает компилятору больше свободы. Вы просто говорите, что хотите, чтобы весь массив заполнялся значением 42. Вы ничего не говорите, в каком порядке это должно быть сделано, или что-то еще.
Ответ 4
Страница, с которой вы связываете состояния
Если указан явный размер массива, но указывается более короткий список инициации, неуказанные элементы устанавливаются равными нулю.
Ошибка скорости. Любые различия были бы незначительными для таких массивов. Если вы работаете с большими массивами, а скорость намного важнее размера, вы можете иметь массив констант по умолчанию (инициализированный во время компиляции), а затем memcpy
их в модифицируемый массив.
Ответ 5
С++ 11 имеет другую (несовершенную) опцию:
std::array<int, 100> a;
a.fill(-1);
Ответ 6
С {} вы назначаете элементы по мере их объявления; остальное инициализируется 0.
Если для инициализации нет = {}
, содержимое undefined.
Ответ 7
Другим способом инициализации массива для общего значения было бы фактически генерировать список элементов из ряда определений:
#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
Ответ 8
1) Когда вы используете инициализатор, для структуры или такого массива, неустановленные значения по умолчанию построены по умолчанию. В случае примитивного типа типа ints это означает, что они будут обнулены. Обратите внимание, что это применяется рекурсивно: у вас может быть массив структур, содержащих массивы, и если вы укажете только первое поле первой структуры, тогда все остальные будут инициализированы нулями и конструкторами по умолчанию.
2) Компилятор, вероятно, сгенерирует код инициализации, который, по крайней мере, так хорош, как вы могли бы сделать вручную. Я предпочитаю, чтобы компилятор выполнил инициализацию для меня, когда это возможно.
Ответ 9
Используя std::array
, мы можем сделать это довольно просто в С++ 14. Это можно сделать только на С++ 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,..., размер - 2. Однако нас не волнует значения (поэтому мы отбрасываем его в пустоту, чтобы отключить любые предупреждения компилятора). Расширение пакета параметров расширяет наш код до чего-то вроде этого (предполагая размер == 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
on, является небольшой оптимизацией. Если кто-то переходит во временный 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.");
}
Ответ 10
В случае массива однобайтовых элементов вы можете использовать memset для установки всех элементов на одно и то же значение.
Вот пример здесь.
Ответ 11
В С++ также возможно использование метапрограмм и вариационных шаблонов. В следующем сообщении показано, как это сделать: Программно создавать статические массивы во время компиляции в С++.
Ответ 12
На языке программирования С++ V4, Stroustrup рекомендует использовать векторы или valarrays над встроенными массивами. С valarrary's, когда вы их создаете, вы можете ввести их в определенное значение, например:
valarray <int>seven7s=(7777777,7);
Чтобы инициализировать массив длиной 7 членов с помощью "7777777".
Это С++ способ реализации ответа с использованием структуры данных С++ вместо массива "простой старый C".
Я переключился на использование valarray как попытку в моем коде, чтобы попытаться использовать С++ 'isms v. C'isms....
Ответ 13
Должна быть стандартной функцией, но по какой-то причине она не включена в стандартный C или С++...
#include <stdio.h>
__asm__
(
" .global _arr; "
" .section .data; "
"_arr: .fill 100, 1, 2; "
);
extern char arr[];
int main()
{
int i;
for(i = 0; i < 100; ++i) {
printf("arr[%u] = %u.\n", i, arr[i]);
}
}
В Fortran вы можете сделать:
program main
implicit none
byte a(100)
data a /100*2/
integer i
do i = 0, 100
print *, a(i)
end do
end
но у него нет неподписанных чисел...
Почему C/С++ не может просто реализовать его. Неужели это так сложно? Так глупо писать это вручную, чтобы достичь того же результата...
#include <stdio.h>
#include <stdint.h>
/* did I count it correctly? I'm not quite sure. */
uint8_t arr = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
};
int main()
{
int i;
for(i = 0; i < 100; ++i) {
printf("arr[%u] = %u.\n", i, arr[i]);
}
}
Что, если это массив из 1000,00 байт? Мне нужно написать script, чтобы написать его для меня, или прибегнуть к хакам со сборкой /etc. Это вздор.
Он отлично переносимый, нет причин, чтобы он не был на языке.
Просто взломайте его, как:
#include <stdio.h>
#include <stdint.h>
/* a byte array of 100 twos declared at compile time. */
uint8_t twos[] = {100:2};
int main()
{
uint_fast32_t i;
for (i = 0; i < 100; ++i) {
printf("twos[%u] = %u.\n", i, twos[i]);
}
return 0;
}
Один из способов взломать его - через предварительную обработку... (Код ниже не охватывает крайние случаи, но написан для быстрого демонстрации того, что можно сделать.)
#!/usr/bin/perl
use warnings;
use strict;
open my $inf, "<main.c";
open my $ouf, ">out.c";
my @lines = <$inf>;
foreach my $line (@lines) {
if ($line =~ m/({(\d+):(\d+)})/) {
printf ("$1, $2, $3");
my $lnew = "{" . "$3, "x($2 - 1) . $3 . "}";
$line =~ s/{(\d+:\d+)}/$lnew/;
printf $ouf $line;
} else {
printf $ouf $line;
}
}
close($ouf);
close($inf);