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

これは、私が使用しているシステムで動作するこのコードにつながります(ただし、コメントでShafik Yaghmourが指摘したように、標準Cへの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[];
};

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()は Microsoft ランド以外では非正統的な C ですが、組み込みシステムで作業していることを暗示しており、それには特別な規則がある可能性があります。void main() { ... }標準のint main(void) { ... }表記法に置き換えました。の使用unsigned int8も非標準ですint8。標準ですが、おそらく組み込みシステムであることから来ています. fromに置き換えunsigned int8ました)uint8_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

これは、さまざまなサイズの柔軟な配列メンバー配列を示し、それらを初期化する 1 つの方法にすぎません。より一般的には、外部デバイス (ディスク上のファイルまたは何らかの I/O チャネル) からサイズおよび初期化データを収集します。

于 2014-07-08T20:11:35.463 に答える
0

これは C99フレキシブル配列メンバーです。

柔軟な配列メンバー...

  • contents[]寸法値なしで書かれている、
  • 型が不完全なため、sizeof 演算子が適用されない場合があります。
  • それ以外の場合は空でない構造体の最後のメンバーとしてのみ表示される場合があります。
  • 柔軟な配列メンバーを含む構造体、またはそのような構造体を (場合によっては再帰的に) 含む共用体は、構造体のメンバーまたは配列の要素にすることはできません。

これにより、可変長オブジェクトのヘッダーとして使用する構造を定義できます。

于 2014-07-08T19:43:45.683 に答える