2016-08-11 61 views
5

为什么有人会这样做?更好的是,这甚至是如何工作的?我会假设这会以某种方式创建一个只有第一个成员定义的三个结构数组。我意识到一个指针指向数组的第一个元素,并且我看到这个如何工作,但是如何定义这个指针会把我扔掉! (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

你应该显示真实的代码。这可能是辅助函数被设计为与数组一起工作。因此,如果数组包含一个元素,则应该将其声明为数组而不是单个结构。 –

+2

没有办法只初始化一个结构的一部分,它是全部或全部。 – molbdnilo

回答

8

我本来以为这会在一定程度创建三个结构的阵列,只有第一个成员定义

不,不是这样的。基本上它创建了一个元素的数组,所有成员都初始化。

引用C11,章§6.7.9

每个括号包围的初始化列表具有相关联的当前对象。当不存在 指定时,当前对象的子对象按照当前对象的类型按照 的顺序进行初始化:数组元素以下标顺序递增,数组元素以声明顺序递增,并且成员为联合的第一个成员。 [...]

每个指示符列表开始其与该 最接近周围的一对括号相关联的当前对象描述。标识符列表中的每个项目(按顺序)指定其当前对象的特定成员,并将当前对象更改为指定的下一个 指定者(如果有)。 150)在 指示符列表结尾处产生的当前对象是要由以下初始化程序初始化的子对象。

[...]如果 一个子聚集初始化或包含联合用左括号开始时,由该 振奋和其匹配的右括号包围的初始化初始化元件或 subaggregate或包含的联合的成员。否则,列表中只有足够的初始值为 是为了考虑子集的元素或成员或所包含联合的第一个成员; [...]

基本上,你的代码应该理想样子

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

想象初始化一个元素。

OTOH,则预期可通过

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

其中它创建3个元素的阵列,所有具有初始化的成员变量a来实现什么。


这就是说,

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

相当于写

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

除部分,my_data不会是一个数组了(但什么好是一个元素的数组,一般来说,要么是?)。

+1

“通常,一个元素数组有多好” - >它允许代码通过“引用”[GMP示例]传递(https://gmplib.org/list-archives/gmp-discuss/2008-March /003085.html)研究如何声明'mpz_t'。 – chux

+0

这个答案基本上是正确的,但如果它也引用了C2011 6.7.9/20,它明确描述了在成员的初始化程序周围没有大括号的情况下的语义,这一点将会更加清楚。 –

+0

@JohnBollinger更新,如果有帮助。 –

2

这是不正确的。海湾合作委员会会给你一个警告:

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

它将做什么是创建一个1元素数组。

+0

海湾合作委员会警告,我同意它,并与你一样,省略成员初始化者的括号是不好的风格。但那是*全部*它是。从符合标准的角度来看,代码是完全正确的。 –

3

编译器将治疗

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

作为

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

所以,my_data是一个the_data_t类型的阵列。

这类似于在一个二维数组声明如下

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

然后编译器会把它当作

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

打印的a的大小,你会得到12到(如果大小int在该机器上是4)。

+0

显然,使用中的特定编译器*不会*按照您所描述的解释初始化程序,但我无法查看标准描述的行为。 –

+0

@JohnBollinger;这在** 6.7.9/20 **部分中有描述:*如果子集合或包含联合的初始化器以左括号开始,则由该大括号和其右对齐大括号包围的初始化器会初始化子集合的元素或成员或包含的联盟。否则,从列表中只有足够的初始化程序被用来说明子集合的元素或成员或所包含工会的第一个成员;任何剩余的初始化器都将被初始化为当前子集合或[*] *的集合的下一个元素或成员。 – haccks

2

如果使用警告开关,gcc会给你警告这个问题。

$ 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}}; 

或者,当你在后显示,你可以改变my_data到结构体变量。

the_data_t my_data = {10, 'a', 30}; // And call do_something as 
do_something((const void *)&my_data);