You're using undefined behaviour and probably running into a compiler bug at the same time. Note that GCC 4.9.0 (compiled on an Ubuntu 12.04 derivative but running on an Ubuntu 14.04 derivative) gives lots of errors for this trivial adaptation of your code:
#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] );
}
}
Compilation errors:
$ 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’)
The fact that you are not getting similar errors suggests that your compiler is rather old, or otherwise not as helpful as it could be. Note that even though I have stream of stringent compiler warning options enabled, the compilation also fails with the same messages with just gcc -c vla.c
(still an error, unconditionally).
You can't have an array of structures with flexible array members; the initialization ought not to be allowed. You can have arrays of pointers to structures containing FAMs, but not arrays of FAMs.
Using a GCC extension
Note that this compiles without warnings (until you add -pedantic
to the compiler options I use):
struct table_type t0 =
{ 0, 1, 2, 3, { 4, 5, 6, 7, 8} };
This leads to this code which works on the system I'm using (but it is a solution using GCC extension to standard C, as pointed out by Shafik Yaghmour in a comment):
#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] );
}
}
Sample output:
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
(Incidentally, void main()
is unorthodox C except in Microsoft-land; however, you imply that you're working in an embedded system and maybe that has special rules. I replaced void main() { ... }
with the standard int main(void) { ... }
notation. The use of unsigned int8
is also non-standard because int8
is not standard, but probably comes from being an embedded system. I replaced unsigned int8
with uint8_t
from <stdint.h>
)
Avoiding the GCC extension
In this example, all the arrays are the same size, so there really is no merit in using the flexible array member notation. The simplest solution to avoiding the problems of GCC extensions, therefore, is to provide the correct size for the array:
#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]);
}
}
Assuming that the flexible array members do in fact need to be different sizes, then you have to use dynamic memory allocation and arrays of pointers to structures containing FAMs.
#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');
}
}
Example output:
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
This is merely one way of demonstrating different size flexible array member arrays, and of initializing them. More typically, you'd collect the size and initialization data from some external device — a file on disk or an I/O channel of some sort.