实现container_of时的指针对齐

时间:2018-05-29 06:06:21

标签: c pointers types memory-alignment

我一直在使用我自己的model - 类型函数,它不依赖于GNU或C99,(即,它适用于不同版本的MSVC。)

container_of

打印出来,

#include <stddef.h> /* offsetof */
#include <stdlib.h> /* EXIT_ */
#include <stdio.h>  /* printf */

/* Abstract Animal. */
struct AnimalVt;
struct Animal {
    const struct AnimalVt *vt;
    char name[16];
};

/* Sloth extends Animal. */
struct Sloth {
    struct Animal animal;
    unsigned hours_slept;
};
/* Modifyable (unused):
 static struct Sloth *sloth_holds_animal(struct Animal *const animal) {
    return (struct Sloth *)
        ((char *)animal - offsetof(struct Sloth, animal));
}*/
static const struct Sloth *
    sloth_holds_const_animal(const struct Animal *const animal) {
    return (const struct Sloth *)
        ((const char *)animal - offsetof(struct Sloth, animal));
}
static void sloth_print(const struct Animal *const animal) {
    const struct Sloth *const sloth = sloth_holds_const_animal(animal);
    printf("Sloth %s has been sleeping %u hours.\n",
        animal->name, sloth->hours_slept);
}

/* Emu extends Animal. */
struct Emu {
    struct Animal animal;
    char favourite_letter;
};
static const struct Emu *
    emu_holds_const_animal(const struct Animal *const animal) {
    return (const struct Emu *)(const void *)
        ((const char *)animal - offsetof(struct Emu, animal));
}
static void emu_print(const struct Animal *const animal) {
    const struct Emu *const emu = emu_holds_const_animal(animal);
    printf("Emu %s has \"%c\" as their favourite letter.\n",
        animal->name, emu->favourite_letter);
}

/* Virtual tables. */
typedef void (*AnimalAction)(const struct Animal *const);
static const struct AnimalVt {
    const AnimalAction print;
} sloth_vt = { &sloth_print }, emu_vt = { &emu_print };

static void print(const struct Animal *const animal) {
    animal->vt->print(animal);
}

int main(void) {
    const struct Sloth bob = { { &sloth_vt, "Bob" }, 10 };
    const struct Emu alice = { { &emu_vt, "Alice" }, 'z' };
    const struct Animal *a[] = { &alice.animal, &bob.animal };
    const size_t a_size = sizeof a / sizeof *a;
    size_t i;
    for(i = 0; i < a_size; i++) print(a[i]);
    return EXIT_SUCCESS;
}

Emu Alice has "z" as their favourite letter. Sloth Bob has been sleeping 10 hours. Emu的中间人;我用它来忘记有关对齐的信息。最近我一直想知道这是否是可疑的; https://wiki.sei.cmu.edu/confluence/display/c/EXP36-C.+Do+not+cast+pointers+into+more+strictly+aligned+pointer+types

C标准,6.3.2.3,第7段[ISO / IEC 9899:2011],陈述,

  

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

没有它,

  

警告:从'const char *'转换为'const struct Sloth *'         增加所需的对齐从1到8 [-Wcast-align]

这是完全合理的。我一直在使用void * - 样式代码。它似乎工作。我应该担心对齐,还是这真的很迂腐?在什么情况下会失败?我可以使用Emu来确保不会发生这种情况吗?有没有办法让assert - 类型函数更健壮?

1 个答案:

答案 0 :(得分:1)

如果您的指针最初指向有效对象,则可以将其转换为void*,然后根据需要将其强制转换为原始类型(例如,参见this online C11 standard draft):< / p>

  

6.3.2.3指针

     

(1)指向void的指针可以转换为指向any的指针   对象类型。指向任何对象类型的指针可以转换为   指向void并再次返回;结果应该等于   原始指针。

因此,您的强制转换序列不会引入未定义的行为。