Инициализация обычного массива с одним значением по умолчанию [дубликат]



этот вопрос уже есть ответ здесь:




  • Как инициализировать все члены массива к одному и тому же значению?

    18 ответов



C++ Примечания: Инициализация Массива имеет хороший список над инициализацией массивов. У меня есть



int array[100] = {-1};


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



код



int array[100] = {0};


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



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



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

735   13  

13 ответов:

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

int array[100] = {-1};

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

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

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

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

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

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

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

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

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

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

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

нет встроенного способа инициализации всего массива до некоторого ненулевого значения.

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

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. Вы ничего не говорите о том, в каком порядке это должно быть сделано, или что-нибудь еще.

C++11 имеет еще один (несовершенный) вариант:

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

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

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

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

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

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

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

#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

используя 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, ... размер - 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 on, это незначительная оптимизация. Если кто-то проходит во временную строку std::и говорит: "Сделайте массив из 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.");
}

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

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

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

вот пример здесь.

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

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

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

для инициализации массива длиной 7 членов с "7777777".

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

я переключился на использование valarray в качестве попытки в моем коде, чтобы попытаться использовать C++'isms V. c'iSMS....

должна быть стандартная функция, но по какой-то причине она не включена в стандарт C или 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]);
    }
}

в Фортране вы могли бы сделать:

program main
    implicit none

    byte a(100)
    data a /100*2/
    integer i

    do i = 0, 100
        print *, a(i)
    end do
end

но у него нет беззнаковых чисел...

почему C / 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]);
    }
}

что, если это был массив из 1,000,00 байт? Мне нужно будет написать сценарий, чтобы написать его для меня, или прибегнуть для взломов с сборкой / 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 (", , ");        
        my $lnew = "{" . ", "x( - 1) .  . "}";
        $line =~ s/{(\d+:\d+)}/$lnew/;
        printf $ouf $line;
    } else {
        printf $ouf $line;
    }
}

close($ouf);
close($inf);

Comments

    Ничего не найдено.