5

以下是否应该对 的声明有效table_type,特别是e[]

struct table_type
{
   unsigned int8 a;
   unsigned int8 b;
   unsigned int8 c;
   unsigned int8 d;
   unsigned int8 e[];
};

struct table_type table[] =
{
{  0,   1,  2,  3, {  4,  5,  6,  7,  8} },
{  9,  10, 11, 12, { 13, 14, 15, 16, 17} },
{ 18,  19, 20, 21, { 22, 23, 24, 25, 26} },
{ 27,  28, 29, 30, { 31, 32, 33, 34, 35} },
{ 36,  37, 38, 39, { 40, 41, 42, 43, 44} },
{ 45,  46, 47, 48, { 49, 50, 51, 52, 53} }
};

void main()
{
   unsigned int8 i = 0;
   unsigned int8 j = 0;

   for( i=0; i<6; i++ )
   {
      printf("\n");
      for( j=0; j<=4; j++ )
         printf( "i=%u j=%u k=%u\n", i, j, table[i].e[j] );
   }
}

所有这些都是打印出表格中每一行的 e 元素。这是输出,这显然是愚蠢的:

i=0 j=0 k=4
i=0 j=1 k=9
i=0 j=2 k=10
i=0 j=3 k=11
i=0 j=4 k=12

i=1 j=0 k=13
i=1 j=1 k=18
i=1 j=2 k=19
i=1 j=3 k=20
i=1 j=4 k=21

i=2 j=0 k=22
i=2 j=1 k=27
i=2 j=2 k=28
i=2 j=3 k=29
i=2 j=4 k=30

i=3 j=0 k=31
i=3 j=1 k=36
i=3 j=2 k=37
i=3 j=3 k=38
i=3 j=4 k=39

i=4 j=0 k=40
i=4 j=1 k=45
i=4 j=2 k=46
i=4 j=3 k=47
i=4 j=4 k=48

i=5 j=0 k=49
i=5 j=1 k=50
i=5 j=2 k=51
i=5 j=3 k=52
i=5 j=4 k=53

请注意,当 时在最后一个块中是正确的i=5。当我用 替换e[]e[5],输出都是正确的。我正在使用 CCS C 编译器和 Microchip 微控制器。我只是好奇这是一个错误还是什么。

4

2 回答 2

8

您正在使用未定义的行为,并且可能同时遇到编译器错误。请注意,GCC 4.9.0(在 Ubuntu 12.04 衍生版本上编译,但在 Ubuntu 14.04 衍生版本上运行)为您的代码的这种微不足道的改编提供了很多错误:

#include <stdio.h>
#include <stdint.h>

struct table_type
{
   uint8_t a;
   uint8_t b;
   uint8_t c;
   uint8_t d;
   uint8_t e[];
};

struct table_type table[] =
{
  {  0,   1,  2,  3, {  4,  5,  6,  7,  8} },
  {  9,  10, 11, 12, { 13, 14, 15, 16, 17} },
  { 18,  19, 20, 21, { 22, 23, 24, 25, 26} },
  { 27,  28, 29, 30, { 31, 32, 33, 34, 35} },
  { 36,  37, 38, 39, { 40, 41, 42, 43, 44} },
  { 45,  46, 47, 48, { 49, 50, 51, 52, 53} }
};

int main(void)
{
   uint8_t i = 0;
   uint8_t j = 0;

   for( i=0; i<6; i++ )
   {
      printf("\n");
      for( j=0; j<5; j++ )
         printf( "i=%u j=%u k=%u\n", i, j, table[i].e[j] );
   }
}

编译错误:

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration -Werror  -c vla.c
vla.c:15:3: error: initialization of flexible array member in a nested context
   {  0,   1,  2,  3, {  4,  5,  6,  7,  8} },
   ^
vla.c:15:3: error: (near initialization for ‘table[0].e’)
vla.c:16:3: error: initialization of flexible array member in a nested context
   {  9,  10, 11, 12, { 13, 14, 15, 16, 17} },
   ^
vla.c:16:3: error: (near initialization for ‘table[1].e’)
vla.c:17:3: error: initialization of flexible array member in a nested context
   { 18,  19, 20, 21, { 22, 23, 24, 25, 26} },
   ^
vla.c:17:3: error: (near initialization for ‘table[2].e’)
vla.c:18:3: error: initialization of flexible array member in a nested context
   { 27,  28, 29, 30, { 31, 32, 33, 34, 35} },
   ^
vla.c:18:3: error: (near initialization for ‘table[3].e’)
vla.c:19:3: error: initialization of flexible array member in a nested context
   { 36,  37, 38, 39, { 40, 41, 42, 43, 44} },
   ^
vla.c:19:3: error: (near initialization for ‘table[4].e’)
vla.c:20:3: error: initialization of flexible array member in a nested context
   { 45,  46, 47, 48, { 49, 50, 51, 52, 53} }
   ^
vla.c:20:3: error: (near initialization for ‘table[5].e’)

您没有收到类似错误的事实表明您的编译器相当旧,或者没有它可能提供的帮助。请注意,即使我启用了严格的编译器警告选项流,编译也会失败,并显示相同的消息gcc -c vla.c(仍然是错误,无条件)。

您不能拥有具有灵活数组成员的结构数组;不应允许初始化。您可以拥有指向包含 FAM 的结构的指针数组,但不能拥有 FAM 数组。

使用 GCC 扩展

请注意,这会在没有警告的情况下编译(直到您添加-pedantic到我使用的编译器选项):

struct table_type t0 =
  {  0,   1,  2,  3, {  4,  5,  6,  7,  8} };

这导致此代码适用于我正在使用的系统(但它是使用 GCC 扩展标准 C 的解决方案,正如Shafik Yaghmour评论中指出的那样):

#include <stdio.h>
#include <stdint.h>

struct table_type
{
   uint8_t a;
   uint8_t b;
   uint8_t c;
   uint8_t d;
   uint8_t e[];
};

struct table_type t0 =
  {  0,   1,  2,  3, {  4,  5,  6,  7,  8} };
struct table_type t1 =
  {  9,  10, 11, 12, { 13, 14, 15, 16, 17} };
struct table_type t2 =
  { 18,  19, 20, 21, { 22, 23, 24, 25, 26} };
struct table_type t3 =
  { 27,  28, 29, 30, { 31, 32, 33, 34, 35} };
struct table_type t4 =
  { 36,  37, 38, 39, { 40, 41, 42, 43, 44} };
struct table_type t5 =
  { 45,  46, 47, 48, { 49, 50, 51, 52, 53} };

struct table_type *pointers[] = { &t0, &t1, &t2, &t3, &t4, &t5 };

int main(void)
{
   uint8_t i = 0;
   uint8_t j = 0;

   for( i=0; i<6; i++ )
   {
      printf("\n");
      for( j=0; j<5; j++ )
         printf( "i=%u j=%u k=%u\n", i, j, pointers[i]->e[j] );
   }
}

样本输出:

i=0 j=0 k=4
i=0 j=1 k=5
i=0 j=2 k=6
i=0 j=3 k=7
i=0 j=4 k=8

i=1 j=0 k=13
i=1 j=1 k=14
i=1 j=2 k=15
i=1 j=3 k=16
i=1 j=4 k=17

i=2 j=0 k=22
i=2 j=1 k=23
i=2 j=2 k=24
i=2 j=3 k=25
i=2 j=4 k=26

i=3 j=0 k=31
i=3 j=1 k=32
i=3 j=2 k=33
i=3 j=3 k=34
i=3 j=4 k=35

i=4 j=0 k=40
i=4 j=1 k=41
i=4 j=2 k=42
i=4 j=3 k=43
i=4 j=4 k=44

i=5 j=0 k=49
i=5 j=1 k=50
i=5 j=2 k=51
i=5 j=3 k=52
i=5 j=4 k=53

(顺便说一句,void main()除了在微软领域之外,C 是非正统的;但是,您暗示您在嵌入式系统中工作,并且可能有特殊规则。我void main() { ... }用标准int main(void) { ... }表示法替换。使用unsigned int8也是非标准的,因为int8不是标准,但可能来自嵌入式系统。我用unsigned int8fromuint8_t替换<stdint.h>

避免 GCC 扩展

在此示例中,所有数组的大小都相同,因此使用灵活的数组成员表示法确实没有任何好处。因此,避免 GCC 扩展问题的最简单解决方案是为数组提供正确的大小:

#include <stdio.h>
#include <stdint.h>

struct table_type
{
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint8_t d;
    uint8_t e[5];
};

struct table_type table[] =
{
    {  0,   1,  2,  3, {  4,  5,  6,  7,  8} },
    {  9,  10, 11, 12, { 13, 14, 15, 16, 17} },
    { 18,  19, 20, 21, { 22, 23, 24, 25, 26} },
    { 27,  28, 29, 30, { 31, 32, 33, 34, 35} },
    { 36,  37, 38, 39, { 40, 41, 42, 43, 44} },
    { 45,  46, 47, 48, { 49, 50, 51, 52, 53} },
};

int main(void)
{
    uint8_t i = 0;
    uint8_t j = 0;

    for (i = 0; i < 6; i++)
    {
        printf("\n");
        for (j = 0; j < 5; j++)
            printf("i=%u j=%u k=%u\n", i, j, table[i].e[j]);
    }
}

假设灵活的数组成员实际上需要不同的大小,那么您必须使用动态内存分配和指向包含 FAM 的结构的指针数组。

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

struct table_type
{
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint8_t d;
    uint8_t len;
    uint8_t e[];
};

struct table_type *pointers[6];

struct table_info
{
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint8_t d;
    uint8_t num;
    uint8_t rep;
    uint8_t info[6];
};

struct table_info data[] =
{
    {  0,   1,  2,  3,  5, 1, {  4,  5,  6,  7,  8,  0, } },
    {  9,  10, 11, 12,  4, 2, { 13, 14, 15, 16,  0,  0, } },
    { 18,  19, 20, 21,  3, 3, { 22, 23, 24,  0,  0,  0, } },
    { 27,  28, 29, 30,  4, 3, { 31, 32, 33, 34,  0,  0, } },
    { 36,  37, 38, 39,  5, 2, { 40, 41, 42, 43, 44,  0, } },
    { 45,  46, 47, 48,  6, 2, { 49, 50, 51, 52, 53, 79, } },
};

int main(void)
{
    for (uint8_t i = 0; i < 6; i++)
    {
        assert(data[i].num * data[i].rep < UINT8_MAX);
        size_t nelem = data[i].num * data[i].rep;
        size_t bytes = sizeof(struct table_type) + nelem * sizeof(pointers[i]->e[0]);
        pointers[i] = malloc(bytes);
        pointers[i]->a = data[i].a;
        pointers[i]->b = data[i].b;
        pointers[i]->c = data[i].c;
        pointers[i]->d = data[i].d;
        pointers[i]->len = data[i].num * data[i].rep;

        uint8_t n = 0;
        for (uint8_t j = 0; j < data[i].rep; j++)
        {
            for (uint8_t k = 0; k < data[i].num; k++)
                pointers[i]->e[n++] = data[i].info[k];
        }
    }

    for (uint8_t i = 0; i < 6; i++)
    {
        printf("index = %2d, a = %2d, b = %2d, c = %2d, d = %2d, len = %2d\n",
               i, pointers[i]->a, pointers[i]->b, pointers[i]->c,
               pointers[i]->d, pointers[i]->len);
        const char *pad = "        ";
        for (uint8_t j = 0; j < pointers[i]->len; j++)
        {
            printf("%s%2d", pad, pointers[i]->e[j]);
            pad = ", ";
        }
        putchar('\n');
    }
}

示例输出:

index =  0, a =  0, b =  1, c =  2, d =  3, len =  5
         4,  5,  6,  7,  8
index =  1, a =  9, b = 10, c = 11, d = 12, len =  8
        13, 14, 15, 16, 13, 14, 15, 16
index =  2, a = 18, b = 19, c = 20, d = 21, len =  9
        22, 23, 24, 22, 23, 24, 22, 23, 24
index =  3, a = 27, b = 28, c = 29, d = 30, len = 12
        31, 32, 33, 34, 31, 32, 33, 34, 31, 32, 33, 34
index =  4, a = 36, b = 37, c = 38, d = 39, len = 10
        40, 41, 42, 43, 44, 40, 41, 42, 43, 44
index =  5, a = 45, b = 46, c = 47, d = 48, len = 12
        49, 50, 51, 52, 53, 79, 49, 50, 51, 52, 53, 79

这只是演示不同大小的灵活数组成员数组并对其进行初始化的一种方式。更典型的是,您会从某个外部设备(磁盘上的文件或某种 I/O 通道)收集大小和初始化数据。

于 2014-07-08T20:11:35.463 回答
0

它是一个 C99灵活的数组成员

灵活的数组成员...

  • 写成contents[]没有尺寸值,
  • 类型不完整,因此 sizeof 运算符可能不适用,
  • 只能作为非空结构的最后一个成员出现。
  • 包含灵活数组成员的结构或包含此类结构的联合(可能是递归的)可能不是结构的成员或数组的元素。

它允许定义一个结构以用作可变长度对象的标头。

于 2014-07-08T19:43:45.683 回答