2016-08-11 22 views
5

なぜ誰かがこれを行うでしょうか?これはどのように機能しますか?私はこれがどうにかして最初のメンバーだけが定義された3つの構造の配列を作成すると仮定していたでしょう。私はポインタが配列の最初の要素を指していることを理解しています、そして、これがどのように機能するのか見ていますが、どう定義されているのですか? (GCC 4.8.4)構造体の配列を初期化すると、単一の要素のすべてのメンバーが初期化されますが、なぜですか?

void do_something(const void *); 

typedef struct{ 

int a; 
char b; 
int c; 

} the_data_t; 

int main(int argc, char *argv[]) 
{ 
    the_data_t my_data[] = {10, 'a', 30}; 
    do_something((const void *)my_data); 
} 

void do_something(const void *data) 
{ 
    printf("data a: %d\ndata b: %c\ndata c: %d\n", ((the_data_t*)data)->a, 
     ((the_data_t*)data)->b, ((the_data_t*)data)->c); 
} 

出力

データ:10
データB:
データc:30

かかわらず、私はそれを変更しました。 。

int main(int argc, char *argv[]) 
{ 
    the_data_t my_data = {10, 'a', 30}; 
    do_something(&my_data); 
} 
+1

実際のコードを表示する必要があります。補助関数は配列を扱うように設計されている可能性があります。したがって、配列に1つの要素が含まれている場合は、単一の構造ではなく配列として宣言する必要があります。 –

+2

構造体の一部だけを初期化する方法はありません。すべてまたは何もありません。 – molbdnilo

答えて

8

私はこれは何とか最初のメンバーだけで3つの構造の配列を作成します想定しているだろうがありません、それはその方法ではない

を定義しました。基本的には、すべてのメンバーが初期化された、1つの要素の配列を作成します。

引用C11、章§6.7.9

各ブレースで囲まれた初期化リストは、関連する現在のオブジェクトを有しています。 指定が存在しない場合、現在のオブジェクトのサブオブジェクトは、 の順番で、現在のオブジェクトの型に順番に初期化されます。配列要素は昇順になり、構造体 は宣言順になり、最初に名前が付けられた共用体のメンバになります。 [...]

各指示リストは 最も近い周囲のブレースのペアに関連付けられた現在のオブジェクトとその説明を始めます。指定子リストの各項目(順番に)は、現在のオブジェクトの特定のメンバー を指定し、次の 指定子(存在する場合)の現在のオブジェクトをそのメンバーに変更します。 150) 指定子リストの末尾にある現在のオブジェクトは、次のイニシャライザによって初期化されるサブオブジェクトです。

[...] subaggregateの初期または含ま組合が左括弧で始まる場合、ブレース 及びその対応する右括弧で囲まれた初期化子は、要素を初期化する、または サブアグリゲートまたは含まれるユニオンのメンバー。さもなければ、リストからの十分なイニシャライザだけが、サブ集約の要素またはメンバ、または含まれるユニオンの の最初のメンバを考慮に入れて、 となります。 [...]

基本的には、あなたのコードはは、理想的には 要素を初期化する視覚化する

the_data_t my_data[] = {{10, 'a', 30}}; 

のようになります。あなたが期待したもの

OTOHは、それは3つの要素、初期化メンバ変数aを有する全ての配列を作成し

the_data_t my_data[] = {{10}, {'a'}, {30}}; 

によって達成することができます。

the_data_t my_data[] = {{10, 'a', 30}}; 

は一部を除いて

the_data_t my_data = {10, 'a', 30}; 

を書くことと等価である、と述べた


my_data(もう配列であるが、1つの要素の配列である何が良いではないだろう、一般に、どちらか?)。

+1

"一般的に、1つの要素の配列はどれくらい良いものか" - > "参照"でコードを渡すことができます[GMPの例](https://gmplib.org/list-archives/gmp-discuss/2008-March/mp3_t.hがどのように宣言されているか調査します。 – chux

+0

この回答は本質的には正しいですが、C2011 6.7.9/20も引用していれば、はるかに明確になります。ここでは、メンバーのイニシャライザの周囲に括弧がない場合のセマンティクスを明示的に説明しています。 –

+0

@JohnBollingerが更新されました。 –

2

これは正しくありません。 GCCはこのためにあなたに警告を与える:それは何でしょうと、1つの要素の配列を作成

warning: missing braces around initializer 
warning: (near initialization for ‘my_data[0]’) 

です。

+0

GCCは警告を発しています。メンバーイニシャライザの周りのカッコを省略すると、スタイルが貧弱になります。しかし、それはすべて*です。このコードは、標準に準拠しているという意味では完全に適切です。 –

3

コンパイラはmy_dataは1つのthe_data_t型の配列である、だから、

the_data_t my_data[1] = {{ 10, 'a', 30 }}; // Though it will raise warning. 

として

the_data_t my_data[] = {10, 'a', 30}; 

を扱います。

2次元配列のサイズならば、(コンパイラはaのサイズを印刷

int a[1][3] = { { 1, 2, 3 } }; 

として扱いますし、あなたが12を取得します。この

int a[][3] = { 1, 2, 3 }; 

のように宣言されたときにこれがに似ていますintはそのマシンで4です)。

+0

明らかに、使用中の特定のコンパイラは、あなたが記述しているようにイニシャライザを解釈しますが、その動作が標準でどのように記述されているのか見ていません。 @JohnBollinger; –

+0

;これはセクション** 6.7.9/20 **で説明されています**サブアセンブリまたは組み込みユニオンのイニシャライザが左中括弧で始まる場合、その中括弧とそれに一致する右中括弧で囲まれたイニシャライザは、または含まれている共用体。さもなければ、リストからの十分なイニシャライザだけがサブ集約の要素またはメンバ、または含まれる共用体の最初のメンバを考慮に入れます。残りのイニシャライザは、現在のサブ集合または[...]。*がある集合体の次の要素またはメンバを初期化するために残されます。 – haccks

2

警告スイッチを使用すると、gccからこの問題についての警告が表示されます。

the_data_t my_data[] = {{10, 'a', 30}}; 

それとも、あなたがポストに示したように、あなたは構造体変数にmy_dataを変更することができます。あなたが適切として配列を初期化するか、この問題を解決するには

$ gcc -Wall test.c 
test.c: In function ‘main’: 
test.c:14:25: warning: missing braces around initializer [-Wmissing-braces] 
the_data_t my_data[] = {10, 'a', 30}; 
         ^
test.c:14:25: note: (near initialization for ‘my_data’) 
$ 

the_data_t my_data = {10, 'a', 30}; // And call do_something as 
do_something((const void *)&my_data); 
関連する問題