web-dev-qa-db-fra.com

Initialisation d'un tableau normal avec une valeur par défaut

Notes C++: Initialisation de tablea a une liste de Nice sur l’initialisation de tableaux. j'ai un

int array[100] = {-1};

s'attendant à ce qu'il soit plein avec -1 mais ce n'est pas le cas, seule la première valeur est et les autres sont des 0 mélangés avec des valeurs aléatoires.

Le code

int array[100] = {0};

fonctionne très bien et met chaque élément à 0.

Qu'est-ce qui me manque ici? Ne peut-on l'initialiser si la valeur n'est pas zéro?

2: L’initialisation par défaut (comme ci-dessus) est-elle plus rapide que la boucle habituelle dans tout le tableau et attribue-t-elle une valeur ou fait-elle la même chose?

225
Milan

En utilisant la syntaxe que vous avez utilisée,

_int array[100] = {-1};
_

dit "définissez le premier élément sur _-1_ et le reste sur _0_" car tous les éléments omis sont définis sur _0_.

En C++, pour les définir tous sur _-1_, vous pouvez utiliser quelque chose comme std::fill_n (à partir de _<algorithm>_):

_std::fill_n(array, 100, -1);
_

En C portable, vous devez rouler votre propre boucle. Il existe des extensions de compilateur ou vous pouvez dépendre du comportement défini par l'implémentation comme raccourci si cela est acceptable.

324
Evan Teran

Il existe une extension au compilateur gcc qui autorise la syntaxe:

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

Cela définirait tous les éléments à -1.

Ceci est connu sous le nom "d'initialisateurs désignés", voir ici pour plus d'informations.

Notez que ceci n'est pas implémenté pour le compilateur gcc c ++.

128
Callum

La page à laquelle vous avez accédé a déjà donné la réponse à la première partie:

Si une taille de tableau explicite est spécifiée, mais qu'une liste d'initiliazation plus courte est spécifiée, les éléments non spécifiés sont définis sur zéro.

Il n'y a pas de moyen intégré pour initialiser le tableau entier à une valeur autre que zéro.

Pour ce qui est plus rapide, la règle habituelle s'applique: "La méthode qui donne le plus de liberté au compilateur est probablement plus rapide".

int array[100] = {0};

indique simplement au compilateur "réglez ces 100 ints à zéro", ce que le compilateur peut optimiser librement.

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

est beaucoup plus spécifique. Il indique au compilateur de créer une variable d'itération i, il lui indique le ordre dans lequel les éléments doivent être initialisés, etc. Bien sûr, le compilateur optimisera probablement cela, mais le fait est que vous spécifiez trop le problème, ce qui oblige le compilateur à travailler plus fort pour obtenir le même résultat.

Enfin, si vous souhaitez définir le tableau sur une valeur autre que zéro, vous devez (au moins en C++) utiliser std::fill:

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

Encore une fois, vous pouvez faire la même chose avec un tableau, mais ceci est plus concis et donne plus de liberté au compilateur. Vous dites simplement que vous voulez que le tableau entier soit rempli avec la valeur 42. Vous ne dites rien dans l'ordre dans lequel cela doit être fait, ni quoi que ce soit d'autre.

31
jalf

C++ 11 a une autre option (imparfaite):

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

Avec {}, vous affectez les éléments tels qu'ils sont déclarés. le reste est initialisé à 0.

S'il n'y a pas de = {} à initialiser, le contenu n'est pas défini.

9
0x6adb015

La page que vous avez liée déclare

Si une taille de tableau explicite est spécifiée, mais qu'une liste d'initiliazation plus courte est spécifiée, les éléments non spécifiés sont définis sur zéro.

Problème de vitesse: toute différence serait négligeable pour des tableaux de cette taille. Si vous travaillez avec de grands tableaux et que la vitesse est beaucoup plus importante que la taille, vous pouvez avoir un tableau const des valeurs par défaut (initialisé à la compilation), puis memcpy les ajouter au tableau modifiable.

8
laalto

Une autre façon d’initialiser le tableau à une valeur commune serait de générer la liste des éléments dans une série de définitions:

#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 )

L'initialisation d'un tableau à une valeur commune peut facilement être effectuée:

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

Remarque: DUPx introduit pour permettre la substitution de macros dans les paramètres du progiciel DUP.

4
Steen

En utilisant std::array, nous pouvons le faire de manière assez simple en C++ 14. Il est possible de faire en C++ 11 seulement, mais légèrement plus compliqué.

Notre interface est une taille de compilation et une valeur par défaut.

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));
}

La troisième fonction est principalement pratique, l’utilisateur n’a donc pas à construire lui-même un std::integral_constant<std::size_t, size>, car il s’agit d’une construction assez verbeuse. Le vrai travail est effectué par l’une des deux premières fonctions.

La première surcharge est assez simple: elle construit un std::array de taille 0. Aucune copie n'est nécessaire, nous la construisons simplement.

La deuxième surcharge est un peu plus délicate. Il transmet la valeur obtenue en tant que source, construit également une instance de make_index_sequence et appelle simplement une autre fonction d'implémentation. A quoi ressemble cette fonction?

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

Ceci construit les premiers arguments de taille - 1 en copiant la valeur que nous avons transmise. Ici, nous utilisons nos index de pack de paramètres variadiques comme quelque chose à développer. Il y a taille - 1 entrées dans ce pack (comme nous l’avons spécifié dans la construction de make_index_sequence), et elles ont les valeurs 0, 1, 2, 3, ..., taille - 2. Cependant, nous ne le faisons pas. nous nous soucions des valeurs (nous lançons donc le paramètre void, pour faire taire les avertissements du compilateur). L’extension du pack de paramètres étend notre code à quelque chose comme ceci (en supposant que size == 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) };

Nous utilisons ces parenthèses pour nous assurer que l’extension du pack variadique ... élargit ce que nous voulons et que nous utilisons également l’opérateur virgule. Sans les parenthèses, il semblerait que nous passions un tas d'arguments à l'initialisation de notre tableau, mais en réalité, nous évaluons l'index, le convertissons en vide, ignorant ce résultat, puis renvoyons une valeur, qui est copiée dans le tableau. .

Le dernier argument, celui que nous appelons std::forward on, est une optimisation mineure. Si quelqu'un passe dans un std :: string temporaire et dit "crée un tableau de 5 de ces", nous aimerions avoir 4 copies et 1 déplacement, au lieu de 5 copies. Le std::forward garantit que nous le faisons.

Le code complet, y compris les en-têtes et certains tests unitaires:

#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

Dans le cas d'un tableau d'éléments à un octet, vous pouvez utiliser memset pour définir tous les éléments sur la même valeur.

Il y a un exemple ici .

3
Steve Melnikoff

En C++, il est également possible d'utiliser la méta-programmation et les modèles variadiques. Le post suivant montre comment faire: Créer par programme des tableaux statiques lors de la compilation en C++ .

1
ingomueller.net

1) Lorsque vous utilisez un initialiseur, pour une structure ou un tableau comme celui-ci, les valeurs non spécifiées sont essentiellement construites par défaut. Dans le cas d'un type primitif comme ints, cela signifie qu'ils seront mis à zéro. Notez que cela s'applique récursivement: vous pouvez avoir un tableau de structures contenant des tableaux et si vous spécifiez uniquement le premier champ de la première structure, tout le reste sera initialisé avec des constructeurs de zéros et par défaut.

2) Le compilateur générera probablement un code d’initialisation au moins aussi bon que vous pourriez le faire manuellement. J'ai tendance à préférer laisser le compilateur faire l'initialisation pour moi, lorsque cela est possible.

1
Boojum

Dans le langage de programmation C++ V4, Stroustrup recommande d'utiliser des vecteurs ou des tableaux de valeurs sur des tableaux intégrés. Avec valarrary, lorsque vous les créez, vous pouvez les initier à une valeur spécifique telle que:

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

Pour initialiser un tableau de 7 membres avec "7777777".

C'est une manière C++ d'implémenter la réponse en utilisant une structure de données C++ au lieu d'un tableau "ancien ancien C".

Je suis passé à utiliser valarray comme tentative dans mon code d'essayer d'utiliser C++ 'isms v. C'isms ....

0
Astara