297

C ++注:配列の初期化には、配列の初期化に関する優れたリストがあります。私は

int array[100] = {-1};

-1でいっぱいになることを期待しますが、そうではありません。最初の値だけがで、残りは0でランダムな値と混合されます。

コード

int array[100] = {0};

正常に動作し、各要素を0に設定します。

ここで何が欠けていますか..値がゼロでない場合、初期化できませんか?

そして2:デフォルトの初期化(上記のように)は、配列全体をループして値を割り当てる通常のループよりも高速ですか、それとも同じことをしますか?

4

13 に答える 13

404

使用した構文を使用して、

int array[100] = {-1};

省略されたすべての要素が に設定されているため、「最初の要素を に設定し-1、残りを に設定する」と言います。00

C++ では、それらをすべて に設定するには、 (from )-1のようなものを使用できます。std::fill_n<algorithm>

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

ポータブル C では、独自のループを展開する必要があります。コンパイラ拡張機能があります。または、それが許容できる場合は、ショートカットとして実装定義の動作に依存できます。

于 2009-06-30T20:14:10.863 に答える
151

次の構文を許可する gcc コンパイラの拡張機能があります。

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

これにより、すべての要素が -1 に設定されます。

これは「Designated Initializers」と呼ばれ、詳細についてはこちらを参照してください。

これは gcc c++ コンパイラには実装されていないことに注意してください。

于 2009-06-30T20:22:06.760 に答える
40

リンク先のページは、最初の部分に対する答えをすでに示しています。

明示的な配列サイズが指定されているが、より短い初期化リストが指定されている場合、指定されていない要素はゼロに設定されます。

配列全体をゼロ以外の値に初期化する組み込みの方法はありません。

どちらが速いかについては、通常のルールが適用されます。「コンパイラに最も自由度を与える方法は、おそらくより高速です」。

int array[100] = {0};

コンパイラーが自由に最適化できるように、「これらの 100 個の int をゼロに設定する」ようにコンパイラーに指示するだけです。

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 で埋めたいと言っているだけです。それを行う順序やその他のことについては何も言いません。

于 2009-06-30T20:18:27.237 に答える
19

C++11 には別の (不完全な) オプションがあります。

std::array<int, 100> a;
a.fill(-1);
于 2014-09-11T09:20:27.147 に答える
10

{} を使用すると、宣言されたとおりに要素を割り当てます。残りは 0 で初期化されます。

初期化するものがない場合= {}、内容は未定義です。

于 2009-06-30T20:13:35.827 に答える
8

リンクしたページの状態

明示的な配列サイズが指定されているが、より短い初期化リストが指定されている場合、指定されていない要素はゼロに設定されます。

速度の問題: この小さな配列では、違いは無視できます。大きな配列を操作し、速度がサイズよりもはるかに重要である場合は、デフォルト値 (コンパイル時に初期化される) の const 配列を使用memcpyしてから、それらを変更可能な配列にすることができます。

于 2009-06-30T20:14:46.137 に答える
8

を使用する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>3 番目の関数は主に便宜上のものであり、ユーザーが自分でa を作成する必要はありません。実際の作業は、最初の 2 つの関数のいずれかによって行われます。

最初のオーバーロードは非常に簡単です。std::arrayサイズ 0 の a を構築します。コピーは必要ありません。構築するだけです。

2 番目のオーバーロードは少しトリッキーです。ソースとして取得した値に沿って転送し、インスタンスを構築し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

これは、渡された値をコピーすることにより、最初の size - 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は、マイナーな最適化です。誰かが一時的な std::string を渡し、「これらを 5 つ並べて作成する」と言った場合、5 つのコピーではなく、4 つのコピーと 1 つの移動が必要になります。により、これ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.");
}
于 2015-12-03T04:53:05.600 に答える
5

配列を共通の値に初期化する別の方法は、一連の定義で要素のリストを実際に生成することです。

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

注: DUP へのパラメータでマクロ置換を有効にするために DUPx が導入されました

于 2014-07-23T08:39:57.587 に答える
4

シングルバイト要素の配列の場合、memset を使用してすべての要素を同じ値に設定できます。

ここに例があります。

于 2009-07-01T07:31:49.533 に答える
1

1) イニシャライザを使用する場合、そのような構造体または配列に対して、未指定の値は基本的にデフォルトで構築されます。int のようなプリミティブ型の場合、それはそれらがゼロになることを意味します。これは再帰的に適用されることに注意してください。配列を含む構造体の配列を持つことができ、最初の構造体の最初のフィールドだけを指定すると、残りはすべてゼロとデフォルトのコンストラクターで初期化されます。

2) コンパイラはおそらく、少なくとも手動で行うのと同じくらい優れた初期化コードを生成します。可能であれば、コンパイラーに初期化を行わせることを好む傾向があります。

于 2009-06-30T20:15:56.727 に答える
1

C++ プログラミング言語 V4 では、Stroustrup は組み込み配列よりもベクトルまたは valarray を使用することを推奨しています。valarary を使用すると、それらを作成するときに、次のような特定の値に初期化できます。

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

"7777777" で長さ 7 メンバーの配列を初期化するには。

これは、「単純な古い C」配列の代わりに C++ データ構造を使用して回答を実装する C++ の方法です。

C++'isms v. C'isms....

于 2014-05-14T01:21:50.927 に答える
1

C++ では、メタ プログラミングと可変個引数テンプレートを使用することもできます。次の投稿は、その方法を示しています: C++ でコンパイル時にプログラムで静的配列を作成します

于 2014-03-13T13:14:52.657 に答える
-1

標準機能だったらいいのに:

#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/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 argc, char **argv) 
{
    int i;

    for(i = 0; i < 100; ++i) {
        printf("arr[%u] = %u.\n", i, arr[i]);
    }
    
    return 0;
}

それが 1,000,000 バイトの配列である場合は、さらにイライラすることになり、おそらく関数を使用して実行時に入力を行うことを選択するようになるでしょう。

それは完全に移植性があり、言語にない理由はありません。

次のようにハックするだけです:

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

それをハッキングする 1 つの方法は、前処理を使用することです... (以下のコードは特殊なケースをカバーしていませんが、何ができるかをすばやく示すために書かれています。)

#!/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);

注:これは2016年からの素朴な(不器用なひざまずく)答えであり、当時は互換性の問題が私を襲っていませんでした. 下位互換性に対処することは悪夢であり、利便性を自動化するためのカスタムツールを維持することも特に楽しいものではないため、言語に「欲しいという理由だけでクールな新機能」を取得することはもう望んでいません。

于 2016-04-03T11:27:10.253 に答える