it-swarm.com.de

Initialisierung eines normalen Arrays mit einem Standardwert

C++ - Hinweise: Array-Initialisierung hat eine nette Liste über die Initialisierung von Arrays. Ich habe ein

int array[100] = {-1};

erwarten, dass es mit -1 voll ist, aber nicht, nur der erste Wert ist und der Rest sind 0er gemischt mit Zufallswerten.

Der Code

int array[100] = {0};

funktioniert gut und setzt jedes Element auf 0.

Was fehlt mir hier .. Kann man es nicht initialisieren, wenn der Wert nicht Null ist?

2: Ist die Standardinitialisierung (wie oben) schneller als die übliche Schleife durch das gesamte Array und weist einen Wert zu, oder führt sie dasselbe aus?

225
Milan

Verwenden Sie die Syntax, die Sie verwendet haben,

int array[100] = {-1};

sagt "setze das erste Element auf -1 und den Rest auf 0", da alle ausgelassenen Elemente auf 0 gesetzt sind.

Um sie alle in C++ auf -1 Zu setzen, können Sie etwas wie std::fill_n (von <algorithm>) Verwenden:

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

In portablem C müssen Sie Ihre eigene Schleife rollen. Es gibt Compiler-Erweiterungen, oder Sie können sich auf implementierungsdefiniertes Verhalten als Verknüpfung verlassen, wenn dies akzeptabel ist.

324
Evan Teran

Es gibt eine Erweiterung des gcc-Compilers, die die Syntax ermöglicht:

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

Dies würde alle Elemente auf -1 setzen.

Dies wird als "Designated Initializers" bezeichnet. Weitere Informationen finden Sie unter hier .

Beachten Sie, dass dies für den gcc c ++ - Compiler nicht implementiert ist.

128
Callum

Die Seite, auf die Sie verlinkt haben, hat bereits die Antwort auf den ersten Teil gegeben:

Wenn eine explizite Arraygröße angegeben wird, aber eine kürzere Initialisierungsliste angegeben wird, werden die nicht angegebenen Elemente auf Null gesetzt.

Es gibt keine integrierte Möglichkeit, das gesamte Array auf einen Wert ungleich Null zu initialisieren.

Was schneller ist, gilt die übliche Regel: "Die Methode, die dem Compiler die größte Freiheit gibt, ist wahrscheinlich schneller.".

int array[100] = {0};

sagt dem Compiler einfach "setze diese 100 ints auf Null", was der Compiler frei optimieren kann.

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

ist viel spezifischer. Es weist den Compiler an, eine Iterationsvariable i zu erstellen, es gibt die Reihenfolge an, in der die Elemente initialisiert werden sollen, und so weiter. Natürlich wird der Compiler das wahrscheinlich optimieren, aber der Punkt ist, dass Sie hier das Problem zu genau spezifizieren und den Compiler dazu zwingen, härter zu arbeiten, um das gleiche Ergebnis zu erzielen.

Wenn Sie das Array auf einen Wert ungleich Null setzen möchten, sollten Sie (zumindest in C++) std::fill Verwenden:

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

Sie können dasselbe auch mit einem Array tun, dies ist jedoch präziser und gibt dem Compiler mehr Freiheit. Sie sagen nur, dass Sie möchten, dass das gesamte Array mit dem Wert 42 gefüllt wird. Sie sagen nichts darüber, in welcher Reihenfolge es ausgeführt werden soll, oder etwas anderes.

31
jalf

C++ 11 hat eine andere (unvollkommene) Option:

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

Mit {} weisen Sie die Elemente so zu, wie sie deklariert sind. der Rest wird mit 0 initialisiert.

Wenn kein = {} Zum Initialisieren vorhanden ist, ist der Inhalt undefiniert.

9
0x6adb015

Die Seite, die Sie verlinkt haben, gibt an

Wenn eine explizite Arraygröße angegeben wird, aber eine kürzere Initialisierungsliste angegeben wird, werden die nicht angegebenen Elemente auf Null gesetzt.

Geschwindigkeitsproblem: Alle Unterschiede wären für Arrays dieser Größe vernachlässigbar. Wenn Sie mit großen Arrays arbeiten und Geschwindigkeit wichtiger ist als Größe, können Sie ein const-Array mit den Standardwerten (die zur Kompilierungszeit initialisiert werden) und diese dann in das änderbare Array memcpy konvertieren.

8
laalto

Eine andere Möglichkeit, das Array mit einem gemeinsamen Wert zu initialisieren, besteht darin, die Liste der Elemente in einer Reihe von Definitionen zu generieren:

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

Das Initialisieren eines Arrays auf einen gemeinsamen Wert ist einfach:

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

Hinweis: DUPx wurde eingeführt, um die Makrosubstitution in Parametern für DUP zu aktivieren

4
Steen

Mit std::array Können wir dies in C++ 14 ziemlich einfach tun. Dies ist nur in C++ 11 möglich, jedoch etwas komplizierter.

Unsere Schnittstelle ist eine Größe zur Kompilierungszeit und ein Standardwert.

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

Die dritte Funktion dient hauptsächlich der Bequemlichkeit, sodass der Benutzer keinen std::integral_constant<std::size_t, size> Selbst erstellen muss, da dies eine ziemlich wortreiche Konstruktion ist. Die eigentliche Arbeit erledigt eine der ersten beiden Funktionen.

Die erste Überladung ist ziemlich einfach: Es wird ein std::array Der Größe 0 erstellt. Es ist kein Kopieren erforderlich, wir erstellen es einfach.

Die zweite Überladung ist etwas kniffliger. Es leitet den Wert weiter, den es als Quelle erhalten hat, und erstellt auch eine Instanz von make_index_sequence Und ruft nur eine andere Implementierungsfunktion auf. Wie sieht diese Funktion aus?

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

Dies konstruiert die ersten Argumente der Größe 1, indem der übergebene Wert kopiert wird. Hier verwenden wir unsere variadischen Parameterpaketindizes nur als Erweiterungselement. Dieses Paket enthält Einträge der Größe 1 (wie bei der Erstellung von make_index_sequence Angegeben) und Werte von 0, 1, 2, 3, ..., Größe 2 sich nicht um die Werte kümmern (also machen wir es ungültig, um Compiler-Warnungen auszuschalten). Die Erweiterung des Parameterpakets erweitert unseren Code auf so etwas (unter der Annahme von 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) };

Wir verwenden diese Klammern, um sicherzustellen, dass die Variadic Pack-Erweiterung ... Das erweitert, was wir wollen, und um sicherzustellen, dass wir den Komma-Operator verwenden. Ohne die Klammern würde es so aussehen, als würden wir eine Reihe von Argumenten an unsere Array-Initialisierung übergeben, aber tatsächlich wird der Index ausgewertet, für ungültig erklärt, das ungültige Ergebnis ignoriert und dann ein Wert zurückgegeben, der in das Array kopiert wird .

Das letzte Argument, das wir std::forward Nennen, ist eine geringfügige Optimierung. Wenn jemand einen temporären std :: string übergibt und sagt "mache ein Array von 5 davon", möchten wir 4 Kopien und 1 Zug anstelle von 5 Kopien haben. Der std::forward Stellt sicher, dass wir dies tun.

Der vollständige Code, einschließlich Überschriften und einiger Komponententests:

#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

Für den Fall eines Arrays von Einzelbyte-Elementen können Sie memset verwenden, um alle Elemente auf den gleichen Wert zu setzen.

Es gibt ein Beispiel hier .

3
Steve Melnikoff

In C++ ist es auch möglich, Metaprogrammierung und verschiedene Vorlagen zu verwenden. Der folgende Beitrag zeigt, wie es geht: Programmgesteuertes Erstellen von statischen Arrays zur Kompilierungszeit in C++ .

1
ingomueller.net

1) Wenn Sie einen Initialisierer für eine Struktur oder ein Array wie dieses verwenden, werden die nicht angegebenen Werte im Wesentlichen standardmäßig erstellt. Im Fall eines primitiven Typs wie ints bedeutet dies, dass sie auf Null gesetzt werden. Beachten Sie, dass dies rekursiv gilt: Sie könnten ein Array von Strukturen haben, die Arrays enthalten. Wenn Sie nur das erste Feld der ersten Struktur angeben, werden alle anderen mit Nullen und Standardkonstruktoren initialisiert.

2) Der Compiler generiert wahrscheinlich einen Initialisierungscode, der mindestens so gut ist, wie Sie es von Hand tun könnten. Ich ziehe es vor, den Compiler die Initialisierung für mich vornehmen zu lassen, wenn dies möglich ist.

1
Boojum

In der Programmiersprache C++ V4 empfiehlt Stroustrup die Verwendung von Vektoren oder Valarrays gegenüber eingebauten Arrays. Mit valarrarys können Sie sie beim Erstellen auf einen bestimmten Wert initialisieren, wie z.

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

So initialisieren Sie ein Array mit einer Länge von 7 Mitgliedern mit "7777777".

Dies ist eine C++ - Methode zum Implementieren der Antwort unter Verwendung einer C++ - Datenstruktur anstelle eines "normalen alten C" -Arrays.

Ich habe das Valarray als Versuch in meinem Code zu verwenden, um zu versuchen, C++ 'Ismen v. C'isms zu verwenden.

0
Astara