【问题标题】:Struct with flexible array member and allocating memory for it具有灵活数组成员的结构并为其分配内存
【发布时间】:2018-04-01 12:35:05
【问题描述】:

我正在阅读由维基百科链接的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 个额外的字节,这不足以存储双精度,但是我不明白为什么它会说 s1 的行为和s2 如果它们被声明为:

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

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

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

【问题讨论】:

  • 我认为你是对的。我认为该文件写得不好。此外,您将在分配时使用 offsetof() 以使实际分配的大小正确。

标签: c flexible-array-member


【解决方案1】:

(您不应该再研究 C99,它已经过时了。C11 是文档 n1570,位于您引用的同一位置。它可能/希望很快会被 C17 取代。)

我认为,它说它表现得好像只有一个元素的原因是短语

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

【讨论】:

  • 我几乎不认为 C99 已经过时,尤其是对于我们这些使用嵌入式工具的人来说,其中一些仍然使用 C89 :(
  • @bd2357,这使得指出相关的最新信息变得更加重要。
【解决方案2】:

我喜欢将 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 编译器生成不同的填充。

【讨论】:

    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-22
    • 2020-04-13
    • 2012-11-22
    • 2011-07-25
    • 1970-01-01
    相关资源
    最近更新 更多