【问题标题】:C initialize array within structureC在结构中初始化数组
【发布时间】:2010-12-06 04:38:55
【问题描述】:

我想在结构中包含一个可变长度数组,但无法正确初始化它。

struct Grid {
  int rows;
  int cols;
  int grid[];
}

int main() {
  struct Grid testgrid = {1, 3, {4, 5, 6}};
}

我尝试的每件事都会给我一个“错误:灵活数组成员的非静态初始化”错误。

【问题讨论】:

  • 我编辑了上面的问题来说明......
  • 哪个编译器,在哪个平台上?
  • 因此,您希望能够创建两个具有不同长度 grid[] 数组的不同 GRid 对象。 sizeof Grid 应该给你什么号码?如果我想创建一个 Grid 对象数组,编译器应该为每个元素分配多少字节?你需要让 grid 成为一个指向 int 的指针。

标签: c struct


【解决方案1】:

您可以通过将结构设置为static 或全局结构来使其在 gcc 中工作,但事实证明,初始化灵活数组成员是不符合标准的,因此除了 gcc 之外它可能无法工作。这是一种仅使用符合 C99 的功能的方法...

#include <stdlib.h>
#include <stdarg.h>

typedef struct Grid {
  int rows;
  int cols;
  int grid[];
} *Grid;

Grid newGrid(int, int, ...);

Grid newGrid(int rows, int cols, ...)
{
Grid g;
va_list ap;
int i, n = rows * cols;

  if((g = malloc(sizeof(struct Grid) + rows * cols * sizeof(int))) == NULL)
    return NULL;
  g->rows = rows;
  g->cols = cols;
  va_start(ap, cols);
  for(i = 0; i < n; ++i)
    g->grid[i] = va_arg(ap, int);
  va_end(ap);
  return g;
}
.
.
.
Grid g1, g2, g3;
g1 = newGrid(1, 1, 123);
g2 = newGrid(2, 3, 1, 1, 1,
                   2, 2, 2);
g3 = newGrid(4, 5, 1,  2,  3,  4,  5,
                   6,  7,  8,  9, 10,
                  11, 12, 13, 14, 15,
                  16, 17, 18, 19, 20);

【讨论】:

  • 如果你把它分开一点,我会+1,这样它的可读性就更好了。 :P
  • 我的意思是把它们分成单独的语句,但这也是个好主意。
  • 我相信你被static 存储类的限制所困扰,因为没有任何方法可以让编译器使用auto 存储类创建可变长度结构。 C99 中有可变长度数组,因此您可以使用可变长度数组在堆栈上分配空间,然后从其地址初始化指向struct Grid 的指针。只要内存是通过struct * 独占访问的,它甚至可能是一个符合要求的程序。你也可以malloc它。然后,您可以将 memcpystatic 结构体转换为 auto 结构体。
  • 对不起,这不是C。我不知道你用的是什么编译器,它支持什么语言扩展,但是你上面写的不是C,C89/90也不是C99。在 C 中,聚合初始值设定项不能用于在结构中“创建”灵活的数组成员,无论这些结构是静态的还是自动的。
  • @DigitalRoss:6.7.8 中的语句仅适用于独立数组,但不适用于结构中的灵活数组成员 (FAM)。从技术上讲,FAM 不是 6.7.8 中的“数组”。此外,如果 6.7.8 的要求适用于 FAM,它们将不仅限于静态数组,对吗?
【解决方案2】:

没有在您的结构中具有可变长度数组 (VLA)。您在结构中拥有的东西称为灵活数组成员。灵活的数组成员与 VLA 完全无关。 C 中存在灵活的数组成员以合法化和支持古老的“struct hack”习语,该习语基于为具有不同大小的尾随数组的结构对象动态分配内存。

灵活的数组成员不能使用聚合初始化器来初始化,这似乎是您在代码中尝试的。您在这里尝试做的事情是根本不可能的。 C中没有这样的功能。

同时,您的编译器生成的错误消息文本似乎表明它支持这样的扩展。这可能是真的,但请记住,这绝不是标准的 C 功能。

【讨论】:

  • 再次,不适用于灵活的数组成员。另请注意,支持此扩展的编译器之一 GCC 清楚且明确地将其记录为特定于 GCC 的扩展。
  • 不用说,在“-ansi -pedantic”模式下调用 GCC 会导致尝试初始化灵活数组成员的警告。
  • 我相信 AudreyT 是正确的——标准在其他地方明确指出 “包含灵活数组成员的结构类型是无法完成的不完整类型。”
  • 叹息,如果你只是简单地引用了我们可以避免讨论的标准。事实证明,有一个灵活数组初始化的例子,标准确实说它是无效的。 6.7.2.1(20)
  • 好吧,老实说,我错过了那个例子。此外,示例通常是非规范性的,因此我跳过了它们。我仍然无法在规范性文本中找到任何明确的措辞来禁止这一点。
【解决方案3】:

这是我的版本:

#include <stdio.h> 

struct matrix {
  int rows;
  int cols;
  int **val;
} a = {        .rows=3,  .cols=1,
        .val = (int*[3]){ (int[1]){1},
                          (int[1]){2},
                          (int[1]){3} } },

  b = {        .rows=3,  .cols=4,
        .val = (int*[3]){ (int[4]){1, 2, 3, 4},
                          (int[4]){5, 6, 7, 8},
                          (int[4]){9,10,11,12} } };

void print_matrix( char *name, struct matrix *m ){
  for( int row=0;row<m->rows;row++ )
    for( int col=0;col<m->cols;col++ )
      printf( "%s[%i][%i]: %i\n", name, row, col, m->val[row][col] );
  puts("");
}

int main(){
  print_matrix( "a", &a );
  print_matrix( "b", &b );
}

【讨论】:

  • 我知道这是一个旧线程,但这只是解决了我遇到的一个大问题。有用的一点是数组可以是不同的大小。我很惊讶在其他任何地方都找不到答案。谢谢。
  • 对我也很有用!其他答案似乎没有解决问题的基本要求。非常感谢!
  • 谢谢。我终于找到了几个月来一直在寻找的临时解决方案。
  • 这个答案最有用的方面是在结构中创建一个 pointer 的想法,但使用 array 复合文字对其进行初始化。如果没有 (int*[]) 演员表,编译器会抱怨。
  • 这似乎不便携。 Gcc 很高兴接受以下内容:typedef struct node { int **data; } node; node n = { (int*[2]) { (int[1]) {5}, (int[1]) {1} } }; 但是 cl.exe (visual studio 2013) 是另一回事。当代码在 .c 文件中时,cl.exe 编译它就好了。但是当它在 .cpp 文件中时,cl.exe 会显示:thing.cpp(15) : error C2059: syntax error : '{' thing.cpp(15) : error C2143: syntax error : missing ';' before '{' thing.cpp(15) : error C2447: '{' : missing function header (old-style formal list?) ...
【解决方案4】:

我不相信这是可能的或支持的。正如DigitalRoss 指出的那样,您可以在static 数组的情况下从文字初始化......尽管我仍然不确定这是否包含在标准中或只是一个常见的扩展。尽管我可以看到gcc explicitly supports it,但我似乎在标准中找不到支持灵活数组的文字初始化的子句。

【讨论】:

  • @Andrey - Comeau 是一个 C++ 编译器,而不是 C 编译器,因此它应该拒绝 C99 代码,因为 C99 标准不包含在 C++ 中。
  • @Chris Lutz:不。Comeau 是一个 C++、C89/90 和 C99 编译器,拒绝你如何调用它。
  • 啊。我以为是“Comeau C++”,但结果是“Comeau C/C++”。我想我在某些时候知道但忘记了。
【解决方案5】:

使用 malloc 的版本:

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

typedef struct Grid {
  int rows;
  int cols;
  int *grid;
} Grid;

/* Should validate params */
Grid
buildGrid(int rows, int cols, int vec[]) {

    Grid grid;
    grid.rows = rows;
    grid.cols = cols;
    int i;

    if ( (grid.grid = malloc(sizeof(vec))) == NULL ) {
        /* do something.*/
    }

    for(i = 0; i < sizeof(vec) ; i++ ) {
        grid.grid[i] = vec[i];
    }

    return grid;
}

【讨论】:

  • sizeof(vec) 不会像你想象的那样工作。数组在作为函数传递时降级为指针,因此该行将与 sizeof(int *) 相同 - 不是您想要的。
  • 另外,还有什么理由不让它成为一个真正的二维数组吗?
  • 克里斯:你是对的。感谢您指出。正确的方法是传递和附加参数 sizeof(vec)/sizeof(vec[0]) 作为 vec 的大小。
  • 既然您将int[] grid 更改为int *grid,您也可以在C99 中使用静态初始化程序并忘记上面的所有废话:struct Grid testgrid = {1, 3, (int[3]){4, 5, 6}};(抱歉恢复,但应该提到这个微不足道的解决方案。 ..)
猜你喜欢
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
相关资源
最近更新 更多