实现 container_of 时的指针对齐
Pointer Alignment when Implementing a container_of
我一直在使用我自己的 container_of
类型的函数,它不依赖于 GNU 或 C99,(也就是说,它适用于不同版本的 MSVC。)
#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
有一个中间转换为 void *
;我用它来让它忘记对齐信息。最近我一直在想这是否值得怀疑; 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],指出,
A pointer to an object or incomplete type may be converted to a
pointer to a different object or incomplete type. If the resulting
pointer is not correctly aligned for the referenced type, the behavior
is undefined.
没有它,
warning: cast from 'const char *' to 'const struct Sloth *'
increases required alignment from 1 to 8 [-Wcast-align]
这是完全合理的。我一直在使用 Emu
风格的代码。它似乎在工作。我应该担心对齐,还是这真的很迂腐?在什么情况下会失败?我可以使用 assert
来确保这不会发生吗?有没有办法让 container_of
类型的函数更健壮?
如果您的指针最初指向一个有效的对象,那么您可以将其转换为 void*
然后再返回原始类型(例如,参见 this online C11 standard draft) :
6.3.2.3 Pointers
(1) A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to the
original pointer.
因此您的转换序列不会引入未定义的行为。
我一直在使用我自己的 container_of
类型的函数,它不依赖于 GNU 或 C99,(也就是说,它适用于不同版本的 MSVC。)
#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
有一个中间转换为 void *
;我用它来让它忘记对齐信息。最近我一直在想这是否值得怀疑; 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],指出,
A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
没有它,
warning: cast from 'const char *' to 'const struct Sloth *' increases required alignment from 1 to 8 [-Wcast-align]
这是完全合理的。我一直在使用 Emu
风格的代码。它似乎在工作。我应该担心对齐,还是这真的很迂腐?在什么情况下会失败?我可以使用 assert
来确保这不会发生吗?有没有办法让 container_of
类型的函数更健壮?
如果您的指针最初指向一个有效的对象,那么您可以将其转换为 void*
然后再返回原始类型(例如,参见 this online C11 standard draft) :
6.3.2.3 Pointers
(1) A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
因此您的转换序列不会引入未定义的行为。