【问题标题】:Strict aliasing in flexible array member?灵活数组成员中的严格别名?
【发布时间】:2019-02-15 15:29:43
【问题描述】:

我正在编写一个 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;
}

Arena 是 Node 的链表,Node 是一个标头,后跟数据 u8 data[]。 u8 是无符号字符。 我维护下一个可用索引 (currentIndex) 并按此索引推进 data 并将其返回为 void * (ptr = &amp;a-&gt;current-&gt;data[a-&gt;currentIndex])。这是否违反了严格的别名规则,因为我正在将指向 u8 的指针转换为其他东西并使用它?

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

基本上,我认为问题可以简化为:如果我 malloc 内存区域的大小为 10,则将指针转换为 struct {int a;} *(假设 4 字节 int),将其设置为 a,什么剩下的 6 个字节会发生什么?它有任何有效的类型吗?灵活数组成员的存在是否会对此产生任何影响?

【问题讨论】:

  • 您实际在哪里访问数据?严格的别名仅适用于取消引用指针时。
  • @Lundin ArenaAlloc 的使用与 malloc 非常相似。例如:int *a = ArenaAlloc(&amp;arena, sizeof(*a)); *a = 1000;.
  • 这本身并不是一个严格的别名问题。仅当您以 uint8_t 访问它,然后以 int 访问它时,问题才会出现。
  • @Lundin 这仅适用于 malloc 的内存,对吧?如果我有一个 uint8_t 局部变量,那么当它作为 int 访问时,即使我以前实际上没有访问它,它也会违反?如果您可以访问具有不同类型的 malloc 内存的不同部分,编译器会跟踪与之相关的偏移量和有效类型?
  • @WingerSendon:有效类型规则是 C99 中最糟糕的部分,据我所知,除了制造混乱和不和谐外,没有任何用处。每一种可能的解释要么产生不可行的极端情况,要么要求编译器放弃规则旨在促进的许多优化,要么两者兼而有之。

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


【解决方案1】:

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

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

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

【讨论】:

  • 所以,给定struct s {int a; short b},如果我这样做:struct s *p = malloc(sizeof(*p)); p-&gt;a=10;,那么p-&gt;b 还没有有效的类型,因为我还没有写(或读)它?如果我像这样访问p-&gt;b*((char *)p + 4 + 1) = 120;,没有问题吗?
【解决方案2】:

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

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

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

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

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

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

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

【讨论】:

  • 这里没有对齐问题。具有灵活数组成员的struct 将为该成员提供足够的填充。
  • @dbush 基于什么?有问题的灵活数组是char 数组,可能根本没有对齐限制,因此可能根本没有填充,因为size_t 和指针值可能会强制结构对齐。如果指针/size_t 组合强制对齐不足以满足任何更大的要求(例如,32 位平台上 SSE 类型数据的 16 字节对齐限制,将灵活数组放在8 字节偏移量...),灵活数组将不会“适合任何对象对齐...”。
  • 来自第 6.7.2.1 节“结构和联合说明符”p18: 作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为灵活数组成员。在大多数情况下,灵活数组成员被忽略。 特别是,结构的大小就像省略了灵活数组成员一样,只是它可能具有比省略所暗示的更多的尾随填充
  • @dbush 你不能假设char 灵活数组是“适当对齐”的,因为“可能”也意味着“可能不是”。例如:code.woboq.org/userspace/glibc/sysdeps/i386/… 将我上面提到的 16 字节对齐强加在 32 位 x86 上。然而,问题中发布的代码仅确保灵活数组的 8 字节对齐。
  • 如果是这种情况,灵活的数组成员将毫无用处。例如,struct s { short b; }; 的大小为 2,struct s { short b; char c[]; }; 的大小为 2,但 struct s { short b; long long c[]; }; 的大小为 8。这就是对齐发挥作用的地方。
猜你喜欢
  • 2015-12-26
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
相关资源
最近更新 更多