あなたは言語の微妙なコーナーに出くわしました。
配列型の式は、ほとんどのコンテキストで、配列オブジェクトの最初の要素へのポインターに暗黙的に変換されます。ここでは適用されない例外は次のとおりです。
&
配列式が単項演算子 (配列全体のアドレスを生成する)のオペランドである場合。
sizeof
単項or (C11 以降)_Alignof
演算子のオペランドの場合 (sizeof arr
は、ポインターのサイズではなく、配列のサイズを返します); と
- 配列オブジェクトの初期化に使用される初期化子の文字列リテラルの場合 (に変換され
char str[6] = "hello";
ません。)"hello"
char*
( N1570ドラフトでは、例外のリストに誤って追加_Alignof
されています。実際、理由は不明ですが_Alignof
、式ではなく型名にのみ適用できます。)
暗黙の仮定があることに注意してください: 配列式は最初に配列オブジェクトを参照するということです。ほとんどの場合、そうです (最も単純なケースは、配列式が宣言された配列オブジェクトの名前である場合です)。ただし、この 1 つのケースで は、配列オブジェクトはありません。
関数が構造体を返す場合、構造体の結果はvalue によって返されます。この場合、構造体には配列が含まれており、少なくとも論理的には、対応する配列オブジェクトのない配列値が返されます。したがって、配列式は、存在しない配列オブジェクトの最初の要素へのポインターに減衰します。bar().c
2011 ISO C 標準では、「一時的な有効期間」を導入することでこれに対処しています。これは、「構造体または共用体に配列型のメンバーが含まれる、構造体または共用体型の非左辺値式」にのみ適用されます ( N1570 6.2.4p8)。このようなオブジェクトは変更できず、その存続期間は、それを含む完全な式または完全な宣言子の最後で終了します。
したがって、C2011 の時点で、プログラムの動作は明確に定義されています。このprintf
呼び出しは、一時的な有効期間を持つ構造体オブジェクトの一部である配列の最初の要素へのポインターを取得します。printf
そのオブジェクトは、呼び出しが終了するまで存在し続けます。
しかし、C99 の時点では、動作は未定義です。引用した句が原因であるとは限りません (私が知る限り、介在するシーケンス ポイントはありません)。C99 では、必要な配列オブジェクトが定義されていないためです。働くことprintf
。
失敗する理由を理解するのではなく、このプログラムを機能させることが目標である場合は、関数呼び出しの結果を明示的なオブジェクトに格納できます。
const struct s result = bar();
printf("%s", result.c);
これで、一時的な保存期間ではなく自動保存期間を持つ構造体オブジェクトができたので、呼び出しの実行中および実行後に存在します。printf