动态数组和预建数据

在C中,我尝试执行以下操作:

typedef struct { int length; int items[]; /* 1 */ } wchararray_t; typedef struct { long hash; wchararray_t chars; /* 2 */ } string_t; static string_t s1 = { 617862378, { 5, { 'H', 'e', 'l', 'l', 'o' } } /* 3 */ }; 

总而言之,我想要一个以另一种类型wchararray_t结尾的类型wchararray_t ,它本身是动态大小的 – 它的大小以length存储。 此外,我还想编写一个预先构建的特定字符串,作为静态数据,这里s1的长度为5。

上面的代码假定C99支持/* 1 */ 。 据我所知,在/* 2 */中将子结构包含在更大的结构中,即使是C99标准也不支持 – 但GCC接受它。 但是, /* 3 */ GCC放弃了:

 error: initialization of flexible array member in a nested context 

作为一种解决方法,上面的理想代码到目前为止被写为以下hack,“有点工作”:

 typedef struct { int length; int items[1]; } wchararray_t; typedef struct { long hash; wchararray_t chars; } string_t; typedef struct { int length; int items[5]; } wchararray_len5_t; typedef struct { long hash; wchararray_len5_t chars; } string_len5_t; static union { string_len5_t a; string_t b; } s1 = { 617862378, { 5, { 'H', 'e', 'l', 'l', 'o' } } }; 

…并且我们使用“s1.b”作为预构建的string_t(并且从不引用“s1.a”,这里仅用于s1的静态声明)。 然而,它打破了最新的GCC 4.8,它优化了部分代码,因为 – 显然 – wchararray_titems上的任何循环只能迭代一次,因为它是一个长度为1的数组。

通过给gcc选择-fno-aggressive-loop-optimizations选项来解决这个特殊问题。 它也可以通过不在wchararray_titems[]数组中声明长度来wchararray_t ,使其成为动态数组“只是因为”。 但是,这种编写代码的方式是一种黑客攻击,我更倾向于以完全不同的方式来解决问题…

(请注意,它是由PyPy生成的所有生成的C代码,而不是手写代码;任何更改都可以,包括它是否需要更改我们在任何地方访问数据的方式,只要“有效”C优化不是防止。)

编辑:用“int []”替换“char []”,它不接受双引号语法"hello" 。 这是因为我正在寻找任何arrays类型的解决方案。

未解决:感谢大家的建议。 似乎没有干净的方法,所以我实现了hackish解决方案:声明类型k + 1次,一次使用灵活的数组“int items [];” 和其他时间用“int items [N];” 对于所需的各种N值。 这需要一些额外的黑客攻击:例如,不使用MSVC的灵活数组(它们的工作方式不同;我没有调查,知道完全相同的语法是否有效); 和GCC遵循C99所说的并且对包含int items[];结构不满意int items[]; 作为唯一的领域。 然而,如果我们添加一个虚拟字段char _dummy[0];它会很高兴char _dummy[0]; ……据我所知,这不是严格意义上的C99 ……

这是hackish,但这可行吗?

 #include  typedef struct { int length; int items[]; /* 1 */ } wchararray_t; typedef struct { long hash; wchararray_t chars; /* 2 */ int dummy[]; /* hack here */ } string_t; static string_t s1 = { 617862378, { 5 }, { 'H', 'e', 'l', 'l', 'o' } /* 3: changed assignment */ }; int main(void) { int i; for (i=0; i < 5; ++i) { putchar(s1.chars.items[i]); } putchar('\n'); return 0; } 

海湾合作委员会给我警告:

 xx.c:10:22: warning: invalid use of structure with flexible array member [-pedantic] xx.c:16:9: warning: initialization of a flexible array member [-pedantic] xx.c:16:9: warning: (near initialization for 's1.dummy') [-pedantic] 

但似乎有效。

参考

编辑 :如何添加“填充成员”,以确保items[]始终正确对齐?

 #include  #include  #include  #include  /* change to the strictest alignment type */ typedef long aligner; typedef struct { long stuff; /* to show misalignment on 64-bit */ int length; aligner padding; int items[]; } chararray_t; typedef struct { long hash; chararray_t chars; int dummy[]; } string_t; static string_t b1 = { 617862378, { 42, 5 }, {-1, -2, -3, -4, -5} }; int main(void) { int i; printf("sizeof chararray_t: %zu\n", sizeof(chararray_t)); printf("offsetof items: %zu\n", offsetof(chararray_t, items)); printf("sizeof string_t: %zu\n", sizeof(string_t)); printf("offsetof dummy: %zu\n", offsetof(string_t, dummy)); for (i=0; i < 5; ++i) { printf("%d ", b1.chars.items[i]); } putchar('\n'); for (i=0; i < 5; ++i) { printf("%d ", b1.dummy[i]); } putchar('\n'); return 0; } 

当我运行上述内容时,我似乎得到了正确的答案:

 sizeof chararray_t: 24 offsetof items: 24 sizeof string_t: 32 offsetof dummy: 32 -1 -2 -3 -4 -5 -1 -2 -3 -4 -5 

回答我自己的问题写下来。 另一个黑客将建立在Alok的建议之上,这可能会偶尔出现虚假对齐 – 然后通过初始化代码修复对齐。 这假设程序中使用的绝大多数此类类型恰好正确对齐。 码:

 typedef struct { long stuff; /* to show misalignment on 64-bit */ int length; int items[]; } chararray_t; typedef struct { long hash; chararray_t chars; int dummy[]; } string_t; static string_t b1 = { 617862378, { 42, 5 }, {-1, -2, -3, -4, -5} }; /* same with b2 .. b6 */ void fixme(void) { /* often compares as equal, and the whole function is removed */ if (offsetof(string_t, dummy) != offsetof(string_t, chars) + offsetof(chararray_t, items)) { static string_t *p_array[] = { &b1, &b2, &b3, &b4, &b5, &b6 }; string_t *p; int i; for (i=0; i<6; i++) { p = p_array[i]; memmove(p->chars.items, p->dummy, p->chars.length * sizeof(int)); } } } 
 #include  typedef struct { int length; char items[]; /* 1 */ } chararray_t; typedef struct { long hash; chararray_t chars; /* 2 */ } string_t; /*static string_t s1 = { 617862378, { 5, { 'H', 'e', 'l', 'l', 'o' } } // 3 };*/ static string_t s1 = { 617862378, {6,"Hello"} /* 3 */ }; int main() { printf("%d %d %s\n",s1.hash,s1.chars.length,s1.chars.items); return 0; } 

为null字符添加1,等等! 🙂

编辑,也适用于2级嵌套(GCC 4.8.0)

 #include  typedef struct { int length; char items[]; /* 1 */ } chararray_t; typedef struct { long hash; chararray_t chars; /* 2 */ } string_t; typedef struct { long number; string_t arr; }experiment_t; static experiment_t s1 = { 617862378, {786,{6,"Hello"}} /* 3 */ }; int main() { printf("%d %d %d %s\n",s1.number,s1.arr.hash,s1.arr.chars.length,s1.arr.chars.items); return 0; } 

———-编辑2 ——————找到一种方法绕过限制C初始化结构中的数组

最终代码::

 #include  typedef struct { int length; int *items; /* 1 */ } intarray_t; typedef struct { long hash; intarray_t chars; /* 2 */ int dummy[2]; } string_t; /*string_t s1 = { 617862378, { 6, {1,2,3,4,5,6} }, { 0,0 } };*/ string_t s1 = {617862378,{},{0,0}}; int main() { int i=0; intarray_t t1 = {.length = 6, .items = (int[6]){1,2,3,4,5,6}}; s1.chars = t1; printf("%d %d\n",s1.hash,s1.chars.length); while(i 

我假设有一些原因要保持字符串“在”结构内“并且你想保存一个字符串,而不是用C字符串初始化。

但是,如果没有,你可以这样做:

 typedef struct { int length; char *items; /* 1 */ } chararray_t; typedef struct { long hash; chararray_t chars; /* 2 */ } string_t; static string_t s1 = { 617862378, { 5, "Hell" } /* 3 */ }; s1.chars.items[4] = 'o' ; 

看起来你可以做联合技巧,但改为使用类型转换?

 #include  typedef struct { int length; int items[]; } wchararray_t; typedef struct { long hash; wchararray_t chars; } string_t; typedef struct { int length; int items[5]; } wchararray_len5_t; typedef struct { long hash; wchararray_len5_t chars; } string_len5_t; static union { string_len5_t a; string_t b; } s5 = { 617862378, { 5, { 'H', 'e', 'l', 'l', 'o' } } }; string_t *s1 = (string_t*) &s5 ; int main( int argc, char *argv[]) { for( int i = 0 ; i < s1->chars.length ; i++ ) { printf ( "%c", s1->chars.items[i] ); } printf( "\n" ); }