2013-08-20 22 views
22

最近この問題が発生しましたが、言語がb = cを許可する理由がわかりませんでした。 b = {3,4}に失敗する。後者を許可することに問題はありますか?あなたは正しいC99やC11 '化合物リテラル' 表記を使用していなかったのでC:宣言後に構造体変数を初期化する

struct T { 
    int x; 
    int y; 
}; 

int main() 
{ 
    T a = {1, 2}; 
    T b; 

    b = {3, 4}; // why does this fail ? 

    T c = {3, 4}; 
    b = c; // this works 

    return 0; 
} 
+3

私はこれを知っていた** dupe:http://stackoverflow.com/q/16614045/694576 – alk

+0

@これはまったくダブではありません。もう一つのリンクは構造体に構造体を割り当てることについて話しますが、これは構造体に初期化子を割り当てることに関するものです。 – user1952500

+1

'b = {3,4};'は初期化ではなく、代入です。前者は 'T b = {3,4} 'です。はい、その2つは異なる獣です... :-)初期化は定義中に行われます。他のすべてのケースで割り当てが行われます。 – alk

答えて

32

ため{3, 4}それは失敗し、それが有効な初期化子だけれども、(少なくとも、それはC言語ではありません。C++の詳細については下記を参照)の式ではありません。

Cのすべての式は、式自体を調べることで判別できるタイプです。 {3, 4}は潜在的にタイプstruct T、またはint[2](配列タイプ)、または他の多くのタイプのいずれかである可能性があります。 (struct T)ないキャストであることを

b = (struct T){3, 4}; 

注:

C99は、式を作成、複合リテラルの初期化子に似た構文を使用していますが、種類を指定できと呼ばれる新機能を追加しましたオペレーター;複合リテラルの構文の一部です。

複合リテラルの詳細については、draft C11 standardのセクション6.5.2.5を参照してください。

複合リテラルは、1999 ISO C標準(C99)によって導入されました。あなたのコンパイラがC99以上(* Cough * Microsoft * Cough *)をサポートしていない場合は、それらを使用することはできません。

C++を使用している場合(別の言語であることを忘れないでください)、複合リテラルはサポートされていませんが、代替語句が存在する可能性があります。 Potatoswatterコメントで指摘するように、この:

b = T{3, 4}; 

はC++ 11(ただし、C++言語の以前のバージョンで)で有効です。これはC++標準のセクション5.2.3 [expr.type.conf]でカバーされています。そのことについては

は、この:

b = {3, 4}; 

も有効なC++ 11構文です。このフォームは、割り当ての右側を含む多くの指定されたコンテキストで使用できます。これは、C++標準のセクション8.5.4 [dcl.init.list]でカバーされています。

最新のC++標準ドラフトの1つはN3485です。

(g ++はC++でC99スタイルの複合リテラルを拡張子としてサポートしています。)

そして、あなたが事前C99コンパイラで立ち往生している場合、あなたはいつものような、独自の初期化関数を書くことができます:それはC99を追加した理由である余分な作業の迷惑量(だ

struct T init_T(int x, int y) { 
    struct T result; 
    result.x = x; 
    result.y = y; 
    return result; 
} 

/* ... */ 

struct T obj; 
/* ... */ 
obj = init_T(3, 4); 

複合リテラル)が、それは仕事をします。一方、ほとんどの場合、あなたはおそらく、初期化を使用した方がいいでしょう:より良いおそらくある

struct T obj; 
/* ... */ 
{ 
    struct T tmp = { 3, 4 }; 
    obj = tmp; 
} 

は、あなたのプログラムが構成されている方法によって異なります。

+3

C++では、カッコで囲まれた型名はラストキャストではありませんが、 'T {3、4}'はそのトリックを行います。 – Potatoswatter

+0

興味深い。なぜコンパイラは初期化と割り当てを別々に処理するのですか?それを初期化している間、RHSは構造体として解釈されているように見えますが、割り当てには不明ですか? – user1952500

+1

@ user1952500:私が言ったように、 '{3,4} 'は初期化されているオブジェクトからその型を取得する有効な初期化子ですが、有効な式ではありません。 –

5

b = (struct T){ 3, 4 }; 

は、参照してください§6.5.2Postfixの事業者と§6.5.2.5複合リテラルで詳細については、ISO/IEC 9899:2011を参照してください。

(type-name) { initializer-list }
(type-name) { initializer-list , }

+0

Microsoft Cコンパイラはこの初期化子を受け入れません。 – JackCColeman

+1

@JackCColeman:最初の文章はこう書いています。それはC99の "複合リテラル"です。 Microsoftは依然としてC89を実装しています(24年前)。 – MSalters

+0

@JackCColeman:MSaltersが言っているように、Microsoft CコンパイラはC89コンパイラなので、C99のこの機能をサポートしていないということは驚くことではありません。確かに、標準を引用する理由の1つは、MSVCの問題を認識している人々(C89コンパイラではなく、C99またはC11ではないことを認識している人々コンパイラ)はそれが動作しないことを知っています。私はC99以降が必要な答えを書くたびにMSをキャストすることを選択しません。私は時間が足りず、役に立たないものは得られません。 –

0

これは言語の定義なので...「これを許可するためにコンパイラを書くのが難しくなる」以外の特別な理由はないと思います。

C++ 11コンパイラを使用している場合は、b = T{3, 4}を実行できます。

+1

C++は複合リテラルをサポートしていません。 'g ++ -std = C++ 11 -pedantic'を使ってみると、"警告:ISO C++は複合リテラルを禁じます "というメッセージが表示されます。 –

+0

それは私が最初に書いたものですが、間違っていると思ったので、他の答えを見てそれを変更しました...あなたの本能を信頼してください... –

関連する問題