[解決済み] C言語における配列の初期化に関する混乱
質問
C言語で配列を初期化する場合、以下のようになります。
int a[5] = {1,2};
とすると、明示的に初期化されていない配列の要素はすべてゼロで暗黙的に初期化されます。
しかし、このように配列を初期化すると
int a[5]={a[2]=1};
printf("%d %d %d %d %d\n", a[0], a[1],a[2], a[3], a[4]);
を出力します。
1 0 1 0 0
よくわからないのですが、なぜ
a[0]
プリント
1
の代わりに
0
? それは未定義の動作ですか?
注意してください。 この質問は、インタビューの中で聞かれたものです。
どのように解決するのですか?
TL;DR:の動作がおかしいと思うのですが。
int a[5]={a[2]=1};
の動作は、少なくとも C99 ではよく定義されていないと思います。
面白いのは、あなたが質問している部分だけが意味をなしていることです。
a[0]
に設定されている
1
というのは、代入演算子は代入された値を返すからです。不明なのはそれ以外の部分です。
もしこのコードが
int a[5] = { [2] = 1 }
であったとしたら、すべてが簡単だったでしょう。
a[2]
を
1
に、それ以外のものは
0
. しかし
{ a[2] = 1 }
では、代入式を含む非指定イニシャライザがあり、ウサギの穴に落ちてしまいます。
今まで見つけたのはこんな感じです。
-
a
はローカル変数でなければなりません。6.7.8 初期化
- 静的記憶期間をもつオブジェクトの初期化子におけるすべての式は,定数式又は文字列リテラルでなければならない。
a[2] = 1
は定数表現ではないのでa
は自動的に保存されなければなりません。 -
a
はそれ自身の初期化でスコープに入っています。6.2.1 識別子のスコープ
- 構造体タグ、ユニオンタグ、列挙タグは、タグを宣言する型指定子において、タグの出現直後から始まるスコープを持ちます。 タグの出現直後から始まります。各列挙定数は、そのスコープを持ちます。 で定義された列挙子の出現直後から始まります。 その他の 他の識別子のスコープは、その宣言子の完了直後から始まります。
宣言子は
a[5]
であるため、変数はそれ自身の初期化においてスコープ内にあります。 -
a
はそれ自身の初期化で生きています。6.2.4 オブジェクトの保存期間
-
識別子がリンクなしで宣言されたオブジェクトで、ストレージクラス 指定子なし
static
は 自動保存期間 . -
可変長配列型を持たないこのようなオブジェクトの場合。 その寿命は そのオブジェクトが関連付けられたブロックに入ることから、そのブロックの実行が終了するまで となります。 となります。(囲まれたブロックに入るか、関数を呼び出すと、現在のブロックの実行は中断されますが、終了しません。 の実行を停止する)。ブロックが再帰的に入力される場合、その都度、新しいインスタンス オブジェクトの新しいインスタンスが毎回作成されます。オブジェクトの初期値は不定です。もし オブジェクトに初期化が指定された場合,ブロックの実行中にその宣言に到達するたびに初期化が実行される。 そうでなければ,宣言に到達するたびに値は不定になる。 そうでなければ,値は,その宣言に到達するたびに不定となる。
-
-
の後にシーケンスポイントがあります。
a[2]=1
.6.8 ステートメントとブロック
-
A
完全な表現
は,他の式や宣言子の一部でない式である。
以下の各項目は完全な式である。
イニシャライザ
式(expression)の中の式(expression)
文の式; 選択文の制御式 (
if
またはswitch
);である。 の制御表現while
またはdo
文の(オプションの)各表現を使用します。 afor
文の中の(オプションの)式はreturn
ステートメントを使用します。 式の終わりはシーケンスポイントです。 式の終わりはシーケンスポイントです。
なお、例えば
int foo[] = { 1, 2, 3 }
では{ 1, 2, 3 }
の部分は中括弧で囲まれた初期化子のリストで、それぞれの初期化子の後にシーケンスポイントがあります。 -
A
完全な表現
は,他の式や宣言子の一部でない式である。
以下の各項目は完全な式である。
イニシャライザ
式(expression)の中の式(expression)
文の式; 選択文の制御式 (
-
初期化は初期化リストの順番で行われます。
6.7.8 初期化
- 中括弧で囲まれた各初期化リストには、関連する カレントオブジェクト . 指定がない場合 指定がない場合、現在のオブジェクトのサブオブジェクトは、現在のオブジェクトの型に従った順序で初期化されます。 配列要素は添え字の増加順に、構造体メンバは宣言順に、ユニオンの最初の名前付きメンバは [...]
- 初期化は、初期化リストの順番で行われ、特定のサブオブジェクトのために提供された各初期化機能は、同じサブオブジェクトのために以前にリストされた初期化機能を上書きします。 特定のサブオブジェクトのために提供される各イニシャライザーは、同じサブオブジェクトのために以前にリストされたイニシャライザーをオーバーライドする。 明示的に初期化されないすべてのサブオブジェクトは,静的記憶期間をもつオブジェクトと同じように暗黙的に初期化されなければならない。 明示的に初期化されないすべてのサブオブジェクトは、静的な保存期間を持つオブジェクトと同じように暗黙的に初期化されるものとします。
-
ただし、イニシャライザ式は必ずしも順番に評価されるとは限りません。
6.7.8 初期化
- 初期化リスト式の中で副作用が発生する順序は不定です。 未定義です。
しかし、それではまだいくつかの疑問が残ります。
-
シーケンスポイントは関係あるのでしょうか。基本的なルールは
6.5 表現
- 前のシーケンスポイントと次のシーケンスポイントの間で、オブジェクトは、その格納された値 変更されることはありません。 式の評価によって . さらに,先行する値 は、格納される値を決定するためにのみ読み込まれなければならない。
a[2] = 1
は式ですが、初期化は式ではありません。これはAnnex Jと若干矛盾しています。
J.2 未定義の動作
- 2つのシーケンスポイント間で、オブジェクトが2回以上変更された場合、または、変更され され、保存される値を決定する以外に以前の値が読み込まれる (6.5)。
Annex Jでは、式による修正だけでなく、どんな修正もカウントされるとあります。しかし、附属書が非規範的であることを考えると、おそらくそれを無視することができます。
-
サブオブジェクトの初期化は、初期化子式に関してどのように順序付けられるのでしょうか?すべてのイニシャライザーは最初に(ある順序で)評価され、次にサブオブジェクトはその結果で(イニシャライザーリストの順序で)初期化されるのでしょうか?または、それらはインターリーブされることができますか?
私が思うに
int a[5] = { a[2] = 1 }
は以下のように実行されます。
-
のストレージ
a
のストレージは、その含むブロックが入力されたときに割り当てられる。この時点では内容は不定です。 -
(唯一の)イニシャライザが実行されます (
a[2] = 1
) が実行され、その後にシーケンスポイントが続きます。これによって1
にa[2]
を返し1
. -
その
1
を初期化するために使用されます。a[0]
(を初期化します(最初のイニシャライザーは最初のサブオブジェクトを初期化します)。
しかし、ここで物事が曖昧になるのは、残りの要素(
a[1]
,
a[2]
,
a[3]
,
a[4]
) は初期化されることになっています。
0
に初期化されるはずですが、いつ初期化されるかは不明です。の前に起こるのでしょうか?
a[2] = 1
が評価される前に起こるのでしょうか?もしそうなら
a[2] = 1
は "勝利" となり
a[2]
しかし、ゼロ初期化と代入式の間にシーケンスポイントがないので、この代入は未定義の動作になるのでしょうか?シーケンスポイントは関係あるのでしょうか(上記参照)?それとも、ゼロ初期化はすべてのイニシャライザが評価された後に行われるのでしょうか?もしそうなら
a[2]
は結局のところ
0
.
C言語規格ではここでの動作を明確に定義していないため、(中略)動作は未定義であると考えています。
関連
-
C 構造体定義エラー: '['トークンの前に一次式があることが予想される
-
[解決済み] Valgrind が初期化されていないバイトについて警告する
-
[解決済み] Xcode - 警告。C99 では関数の暗黙の宣言は無効です。
-
[解決済み] 配列から特定の項目を削除するにはどうすればよいですか?
-
[解決済み] JavaScript で配列に値が含まれているかどうかを確認するにはどうすればよいですか?
-
[解決済み] 配列からArrayListを作成する
-
[解決済み] 配列に特定のインデックスで項目を挿入する方法 (JavaScript)
-
[解決済み] ArrayListの初期化を1行で行う。
-
[解決済み] 新しい配列を作成せずに、既存のJavaScript配列を別の配列で拡張する方法
-
[解決済み】オブジェクトの配列を文字列のプロパティ値でソートする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[C] Error [Error] 代入の左オペランドとして lvalue が必要です。
-
Solve Dev-c++ [エラー] 'for' ループの初期宣言は、C99 または C11 モードでのみ許可されます。
-
警告:符号付き整数式と符号なし整数式の比較 [-Wsign-compare]
-
[解決済み] Valgrind が初期化されていないバイトについて警告する
-
[解決済み] c または c++ 用のシンプルな 2 次元クロスプラットフォームグラフィックスライブラリ?[クローズド]
-
[解決済み] C言語で関数型プログラミングを行うためのツールにはどのようなものがありますか?
-
[解決済み] Cコードの単体テスト【終了しました
-
[解決済み] C言語のi++と++iに性能差はあるのでしょうか?
-
[解決済み] アセンブリがCより速いのはどんなとき?[クローズド]
-
[解決済み] C 言語の配列へのポインタ/ポインタの配列の曖昧さ解消