25

在 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 */
};

简而言之,我想要一种以另一种本身动态调整大小的类型string_t结尾的类型——它的大小存储在. 此外,我还想编写一个预先构建的特定字符串,作为静态数据,这里长度为 5。wchararray_tlengths1

上面的代码假定 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 中中断,它优化了我们的部分代码,因为——显然——任何items对 a 的wchararray_t循环都只能迭代一次,因为它是一个长度为 1 的数组。

通过为 gcc 提供选项来解决此特定问题-fno-aggressive-loop-optimizations。它也可以通过不声明数组中的长度来修复wchararray_titems[]使其成为“仅仅因为”的动态数组。但是,这种编写代码的方式是一种 hack,我宁愿用一种完全不同的方式来解决这个问题......

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

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

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

4

4 回答 4

2

回答我自己的问题并写下来。另一个技巧是建立在 Alok 的建议之上,这可能会偶尔产生虚假对齐 --- 然后通过 init-time 代码修复对齐。这假设程序中使用的大多数此类类型恰好正确对齐。代码:

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));
        }
    }
}
于 2013-04-15T21:29:58.783 回答
2

这很hackish,但这可以工作吗?

#include <stdio.h>

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

GCC 给我警告:

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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

/* 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 
于 2013-04-15T17:55:43.633 回答
1
#include <stdio.h>
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;
}

为空字符加 1,瞧!:)

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

#include <stdio.h>
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 <stdio.h>
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<s1.chars.length)
    {
        printf("%d",s1.chars.items[i]);
        i++;
    }
    putchar('\n');
    return 0;
}
于 2013-04-15T14:35:32.090 回答
0

我认为有一些理由将字符串保留在结构“内部”并且您希望通过不使用 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 <stdio.h>

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" );
}
于 2013-04-15T14:32:56.670 回答