web-dev-qa-db-ja.com

1つの値で2D配列全体を初期化する

次の宣言で

int array[ROW][COLUMN]={0};

私はすべてゼロの配列を取得しますが、次のものがあります

int array[ROW][COLUMN]={1};

私はすべて1つの値を持つ配列を取得しません。デフォルト値はまだ0です。

なぜこの動作と、すべて1で初期化できますか?

編集:memsetに値1を使用すると、各バイトが1に設定されるため、各配列セルの実際の値は1ではなく16843009になることを理解しました。 1に設定するにはどうすればよいですか?

60
Kraken

int array [ROW][COLUMN] = {1};notが「すべてのアイテムを1つに設定する」ことを意味するため、この動作が発生します。これがどのように機能するかを段階的に説明してみましょう。

配列を初期化する明示的で過度に明確な方法は次のようになります。

#define ROW 2
#define COLUMN 2

int array [ROW][COLUMN] =
{
  {0, 0},
  {0, 0}
};

ただし、Cを使用すると、配列(または構造体/共用体)の一部の項目を除外できます。たとえば、次のように書くことができます。

int array [ROW][COLUMN] =
{
  {1, 2}
};

つまり、最初の要素を1および2に初期化し、残りの要素を「静的な保存期間があるかのように」初期化します。 Cには、プログラマーによって明示的に初期化されていない静的ストレージ期間のすべてのオブジェクトをゼロに設定する必要があるという規則があります。

したがって、上記の例では、最初の行は1,2に設定され、次の行は0,0に設定されます。明示的な値を指定しなかったためです。

次に、Cには緩いブレーススタイルを許可するルールがあります。最初の例は次のように書くこともできます。

int array [ROW][COLUMN] = {0, 0, 0, 0};

もちろんこれは貧弱なスタイルですが、読んで理解するのは難しいです。しかし、このルールは便利です。

int array [ROW][COLUMN] = {0};

つまり、「最初の行の最初の列を0に初期化し、他のすべてのアイテムを静的な保存期間があるように初期化します。つまり、ゼロに設定します。」

したがって、あなたがしようとした場合

int array [ROW][COLUMN] = {1};

「最初の行の最初の列を1に初期化し、他のすべての項目をゼロに設定する」ことを意味します。

86
Lundin

配列を-1に初期化する場合は、次を使用できます。

memset(array, -1, sizeof(array[0][0]) * row * count)

ただし、これは0および-1のみで機能します

23
user2021512
int array[ROW][COLUMN]={1};

これはfirst要素のみを1に初期化します。他のすべては0を取得します。

最初のインスタンスでは、あなたは同じことをしています-最初の要素を0に初期化し、残りはデフォルトで0になります。

その理由は簡単です。配列の場合、コンパイラは、指定しないすべての値を0で初期化します。

char配列では、memsetを使用してすべてのバイトを設定できますが、これは一般にint配列では機能しません(ただし、0でも問題ありません)。

一般的なforループはこれをすばやく行います。

for (int i = 0; i < ROW; i++)
  for (int j = 0; j < COLUMN; j++)
    array[i][j] = 1;

または、おそらくより高速(コンパイラーに依存)

for (int i = 0; i < ROW*COLUMN; i++)
  *((int*)a + i) = 1;
11
teppic

GCCには 指定された初期化子 表記の拡張機能があることに注意してください。これはコンテキストに非常に役立ちます。また、コメントなしのclangでも許可されます(GCCとの互換性を保とうとするためです)。

拡張表記により、...を使用して、次の値で初期化される要素の範囲を指定できます。例えば:

#include <stdio.h>

enum { ROW = 5, COLUMN = 10 };

int array[ROW][COLUMN] = { [0 ... ROW-1] = { [0 ... COLUMN-1] = 1 } };

int main(void)
{
    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < COLUMN; j++)
            printf("%2d", array[i][j]);
        putchar('\n');
    }
    return 0;
}

当然、出力は次のとおりです。

 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1

Fortran 66(Fortran IV)には、配列の初期化子の繰り返しカウントがあることに注意してください。指定されたイニシャライザが言語に追加されたときにCがそれらを取得しなかったことは奇妙だといつも思っています。また、Pascalは0..9表記を使用して0から9までの範囲を指定しますが、Cは..をトークンとして使用しないため、使用されなかったことは驚くことではありません。

...表記の前後のスペースは本質的に必須であることに注意してください。数字に付けられている場合、その数字は浮動小数点数として解釈されます。たとえば、0...90...9としてトークン化され、浮動小数点数は配列添え字として許可されません。名前付き定数を使用すると、...ROW-1は問題を引き起こしませんが、安全な習慣を身に付けた方がよいでしょう。


補遺:

GCC 7.3.0が拒否することに注意してください:

int array[ROW][COLUMN] = { [0 ... ROW-1] = { [0 ... COLUMN-1] = { 1 } } };

スカラー初期化子1error: braces around scalar initializer [-Werror])の周りに追加のブレースがあります。標準で明示的に許可されているint a = { 1 };のスカラーの周りに通常中括弧を指定できることを考えると、それが正しいかどうかはわかりません。私もそれが間違っているとは思いません。

また、より良い表記法は[0]...[9]であるのだろうか—それは明確で、他の有効な構文と混同することはできず、浮動小数点数との混乱を避けます。

int array[ROW][COLUMN] = { [0]...[4] = { [0]...[9] = 1 } };

たぶん、標準化委員会はそれを考慮するでしょうか?

5