在Visual Studio中使用双结构hack(灵活数组成员)的C代码

时间:2017-06-22 02:45:01

标签: c visual-studio

我正在尝试测试Github代码,它有结构黑客技术,如下所示。

Phoenix

但是当我尝试在“Visual Studio C ++”中编译并运行此代码时,它在第11行给出了错误:

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

typedef struct Node {
    int wcount;
    double weights[];
}Node;

typedef struct Layer {
    int ncount;
    Node nodes[];
}Layer;

void main()
{
    Node* n = (Node*)malloc(sizeof(Node));
    printf("%d\n", sizeof(n));
}

但是这段代码适用于用gcc编译的Linux系统,并且肯定没有语法错误。 我想知道为什么它不起作用,我该怎么做才能在Visual Studio中使用它。

4 个答案:

答案 0 :(得分:1)

这不是“struct hack”,这是一种旧的,过时的方法,在结构的末尾编写类似int array[1];的东西 - 这是完全不安全,不可移植和未定义的行为。

在20世纪90年代,GCC通过在结构的末尾声明一个零长度数组来实现一个非标准的扩展来解决这个问题。这也是一种过时的技术。

在1999年,当C标准引入了一个名为 flexible array member 的东西时,问题终于得到了永久性的解决。它们定义明确,语法为type array[];

你必须像这样为他们分配空间:

typedef struct
{
  ...; // various data
  int array[]; // flexible array member
}

type_t var = malloc( sizeof(var) + sizeof(int[n]) );

除上述之外的所有其他内容都是脏黑客或非标准C,因此不应使用。

您的代码无效的原因可能是因为您没有在同一个malloc调用中为struct + flexible数组成员分配空间。

此外,在C模式下的VS无可救药地过时了。从1999年开始,他们仍然很难实现所有C语言功能。根据您的VS版本,它可能不完全支持C语言。在2017年,我不建议使用这种旧的C99前编译器,当有现代免费提供时。

答案 1 :(得分:0)

如果您使用MSVC2015 +并且此结构不是公共API的一部分,您可以使用带有MS后端的clang前端。我在Linux / Windows项目中已经使用了2年以上,这给我带来了很大的痛苦。

与问题无关,但是,不要在C中强制转换malloc返回值,只有在打算用C ++编译器进行编译时才需要它。

错过了一个关键部分: 您不能在灵活的结构中拥有灵活的结构成员,因为编译器不知道它的大小。所以基本上,当Node定义很好时,Layer会失败。任何给定的结构可以仅包含一个柔性构件并且仅作为最后一个,这种定义意味着在结构变为柔性构件的任意数量的元件之后。开发人员有责任分配足够的内存并跟踪成员的数量。

所以上面的代码可以用这种方式重写:

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

#define MAX_WEIGHTS 16

typedef struct Node {
  int wcount;
  double weights[];
} Node;

typedef struct Layer {
  int ncount;
  Node *nodes;
} Layer;

void main() {
  Node* n = malloc(sizeof(Node) + MAX_WEIGHTS * sizeof(double));
  printf("%d\n", sizeof(n));
  free(n);
}

显然,您可以用一些运行时值替换MAX_WEIGHTS。

在柔性阵列成员上详细说明:

typedef struct {
  int a;
  double b[];
} A;

typedef struct {
  float c;
  A a;
} B;

typedef struct {
  float c;
  A as[];
} C;

typedef struct {
  float d;
  A;
} D;

int main() {
  A as[12];
  (void)as;
  return 0;
}

使用clang3.9和以下标志编译此代码:

-Wall -Wextra -pedantic -std=c11 -fms-extensions -Wno-microsoft-anon-tag -Werror
test.c:8:5: error: 'a' may not be nested in a struct due to flexible array member [-Werror,-Wflexible-array-extensions]
  A a;
    ^
test.c:13:7: error: 'A' may not be used as an array element due to flexible array member [-Werror,-Wflexible-array-extensions]
  A as[];
      ^
test.c:22:7: error: 'A' may not be used as an array element due to flexible array member [-Werror,-Wflexible-array-extensions]
  A as[12];
      ^

我确实发现第一个错误有点奇怪,因为从内存布局来看,B和D应该是相同的。但是,D是Microsoft语言扩展,不应在可移植代码中使用。

答案 2 :(得分:-1)

struct中的unsized数组是非标准的。在VS 6之前,可以使用MS非标准type item[0];语法,但后来他们删除了该语法。

正确的语法是Node nodes[1];

答案 3 :(得分:-1)

由于C是一种简单的语言,您将不得不手动执行复杂的操作。跟踪节点中有多少重量项并分配正确的字节数现在是您的问题。

这样的事情怎么样:

typedef struct Node {
    int wcount;
    double *weights;
}Node;


void main()
{
    int wcount = 10;
    Node* n = (Node*)malloc(offsetof(Node, Weights) + sizeof(double)* wcount);
    n->Weights[8] = 123; 
}

计算将确定在权重字段开始之前需要多少字节,然后只需乘以需要的权重数量,即可得出总数量。然后你就可以使用指针,就好像它是一个正确数字的数组一样。