使用灵活的数组成员并为其分配内存

时间:2018-04-01 12:35:05

标签: c flexible-array-member

我正在阅读由维基百科链接的this version of the C99 standard,试图了解阵列成员的灵活性。

在6.7.2.1节中,声明了这个结构:

struct s { int n; double d[]; };

给出了一个例子:

s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);

它说s1s2的行为就像宣言是:

struct { int n; double d[1]; } *s1, *s2;

它列出了一些你可以做的事情:

double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior

我可以看到为什么上面的最后一行是未定义的,因为s2只分配了6个额外的字节,这不足以存储一个double,但是我不明白为什么会说这个行为s1s2如果被声明为:

struct { int n; double d[1]; } *s1, *s2;

s2似乎没有分配足够的内存来存储该结构时。

该文件似乎是某种草案,所以我不确定是否存在错误,或者我是否误解了其含义。

2 个答案:

答案 0 :(得分:1)

(你不应该再看C99了,它已经过时了.C11是你引用的同一个地方的n1570文件。很可能/希望很快被C17取代。)

我认为原因是它表现得好像有一个元素就是短语

  

如果它没有元素,那么这样的数组就好像它有一个元素一样   如果尝试访问该元素,则行为未定义...

答案 1 :(得分:0)

我喜欢在C99中像C ++中的虚拟类一样处理灵活的结构,你可以指出它们但你不应该实例化它们。我经常构建一个辅助函数(工厂助手?),它获取灵活数组中存储的项目数,并返回结构所需的实际分配,以确保它清楚意图。

#define OBJECTS_NEEDED 10

typedef struct
{
    uint8_t val1;
    uint32_t val2; // probable forces 4byte alignment of structure 
} myObject_t;

typedef struct
{
    uint8_t allocatedObjects;
    uint16 someMetaData;
    myObject_t objects[];   // struct inherits worst case alignment rule
} myObjectSet_t;

size_t getMyObjectSetSize(uint8_t reqObjs)
{
    return sizeof(myObjectSet_t) + reqObjs * sizeof(myObject_t);
}

void initMyObjectSetSize(myObjectSet_t *mySet, uint8_t reqObjs) 
{
   mySet->allocatedObjects = reqObjs;
   // Other Init code .....
}

void main()
{
    myObjectSet_t *mySet = malloc(getMyObjectSetSize(OBJECTS_NEEDED));
    initMyObjectSetSize(mySet , OBJECTS_NEEDED);
    // One issue, you can't rely on structure padding to be the same
    // from machine to machine so this test may fail on one compiler
    // and pass on another. You really do nee to use offsetof() if 
    // you need to find address of mySet given the address of one
    // of the objects[]
    assert((void*)mySet->objects == (void*)(mySet + 1)); 

}

编译器知道sizeof(myObjectSet_t)至少需要为mod 4,因为myObject_t数组可能需要至少32位对齐。我已经看到在Windows上运行的GNU编译器生成与在同一台笔记本电脑上的虚拟机中运行的GNU编译器不同的填充。