灵活数组成员中的严格混叠?

时间:2019-02-15 15:29:43

标签: c strict-aliasing flexible-array-member

我正在编写一个Arena Allocator,它可以工作,但是我觉得它违反了严格的别名规则。我想知道我是对还是错。这是代码的相关部分:

typedef struct ArenaNode ArenaNode;
struct ArenaNode {
    ArenaNode *next;
    size_t dataSize;
    u8 data[];
};

typedef struct {
    ArenaNode *head;
    ArenaNode *current;
    size_t currentIndex;
} Arena;

static ArenaNode *ArenaNodeNew(size_t dataSize, ArenaNode *next)
{
    ArenaNode *n = malloc(sizeof(ArenaNode) + dataSize);
    n->next = NULL;
    n->dataSize = dataSize;
    return n;
}

void *ArenaAlloc(Arena *a, size_t size)
{
    const size_t maxAlign = alignof(max_align_t);
    size_t offset = nextHigherMultiplePow2(offsetof(ArenaNode, data), maxAlign) - offsetof(ArenaNode, data);
    size_t dataSize = offset + max(size, ARENA_SIZE);
    // first time
    void *ptr;
    if (a->head == NULL) {
        ArenaNode *n = ArenaNodeNew(dataSize, NULL);
        a->head = n;
        a->current = n;
        ptr = n->data + offset;
        a->currentIndex = nextHigherMultiplePow2(offset + size, maxAlign);
    } else {
        // enough space
        if (a->currentIndex + size <= a->current->dataSize) {
            ptr = &a->current->data[a->currentIndex];
            a->currentIndex = nextHigherMultiplePow2(a->currentIndex + size, maxAlign);
        } else {
            ArenaNode *n = ArenaNodeNew(dataSize, NULL);
            a->current->next = n;
            a->current = n;
            ptr = n->data + offset;
            a->currentIndex = nextHigherMultiplePow2(offset + size, maxAlign);
        }
    }
    return ptr;
}

竞技场是节点的链接列表,节点是标头,后跟数据u8 data[]。 u8是无符号字符。 我维护下一个可用索引(currentIndex)并通过该索引前进data并将其作为void *ptr = &a->current->data[a->currentIndex])返回。这是否违反严格的别名规则,因为我正在将指向u8的指针转换为其他指针并使用该指针?

我的困惑来自于事实,即malloc返回的内存没有有效的类型。但是由于我将malloc的指针强制转换为ArenaNode *并在分配它的数据成员(nextdataSize)(在ArenaNodeNew中)之后,因此有效类型成为ArenaNode。还是呢?我没有设置data字段。

基本上,我认为问题可以简化为:如果我分配了一个大小为10的内存区域,将指针强制转换为struct {int a;} *(假设int为4字节),则将其设置为a什么,剩下的6个字节会怎样?有有效的类型吗?灵活数组成员的存在是否以任何方式影响这一点?

2 个答案:

答案 0 :(得分:1)

作为可变数组成员一部分的额外字节在您写入时将具有该成员的有效类型。

您可以安全地将ptr声明为u8 *,并定义函数以返回该类型。

在分配10个字节并将前4个字节视为给定类型的结构的示例中,其余字节尚无有效类型。您可以将它们用于任何类型,前提是您使用的指针正确对齐,即,您可以将int *指向以下字节,但不能指向long long *。由于对齐。

答案 1 :(得分:1)

  

这是否违反严格的别名规则,因为我正在将指向u8的指针转换为其他指针并使用该指针?

不,您没有违反严格的别名,但是您的代码可能违反了7.22.3 Memory management functions, paragraph 1施加的约束:

  

如果分配成功,则返回的指针将进行适当对齐,以便可以将其分配给具有基本对齐要求的任何类型的对象的指针,然后将其用于在分配的空间中访问此类对象或此类对象的数组...

您似乎并不确定要确保对任何对象使用的内存都“适当对齐”。鉴于6.3.2.3 Pointers, paragraph 7的声明:

  

指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未针对引用的类型正确对齐,则行为未定义。

您似乎冒着未定义行为的危险。

“适当对齐”非常依赖平台。