可以将联合内的对齐结构强制转换为联合以访问对齐字段吗?
Can aligned structs inside a union be cast to the union to access aligned fields?
我想弄清楚你从 C99 中对齐变量的地役权中得到了什么:
Exception to strict aliasing rule in C from 6.5.2.3 Structure and union members
如果原始写入是通过指向如下所示对齐结构之一的指针完成的,是否可以让您全权投射到该联合?
#include <stdio.h>
#include <stdlib.h>
struct Foo { char t; int i; };
struct Bar { char t; float f; };
union FooBar {
struct Foo foo;
struct Bar bar;
};
void detector(union FooBar *foobar) {
if (((struct Foo*)foobar)->t == 'F')
printf("Foo %d\n", ((struct Foo*)foobar)->i);
else
printf("Bar %f\n", ((struct Bar*)foobar)->f);
}
int main() {
struct Foo *foo = (struct Foo*)malloc(sizeof(struct Foo));
struct Bar *bar = (struct Bar*)malloc(sizeof(struct Bar));
foo->t = 'F';
foo->i = 1020;
detector((union FooBar*)foo);
bar->t = 'B';
bar->f = 3.04;
detector((union FooBar*)bar);
return 0;
}
注意在第二次调用中,t
被写为 "bar's t" 但是为了辨别它属于哪种类型,检测器会读取它作为 "foo's t"
我对 C++ 的反应是,只有拥有 "allocated it as a FooBar union in the first place" 才能做到这一点。认为这是合法的对我来说是违反直觉的,但对于 C 中的动态分配,没有这样的事情。因此,如果您不能这样做,那么在此异常下,您究竟可以使用上述动态内存分配做什么?
如果 Foo
和 Bar
有不同的对齐方式,你不应该仅仅因为这个原因就这样做。 union
将具有两者中最大的对齐方式,而使用较小值的那个会给你一个未正确对齐的 union
。
你的代码不是别名规则的好例子,因为你这里基本上没有别名。但总的来说,在可能出现别名的情况下,转换为另一种类型总是不好的。您的编译器可能会假设代码看到两个(或更多)指针。如果它们是不同的类型(char
类型除外),编译器可以假定它们永远不会指向同一个对象。
如果你做了这样的事情:
struct Foo foo;
struct Bar bar;
...
detector((union FooBar*)&foo);
detector((union FooBar*)&bar);
然后你可能会遇到对齐问题,因为编译器可能会以一种可能无法正确对齐另一个的方式将这些结构中的每一个放在堆栈上。
但是因为在您的情况下您是为每个结构动态分配内存,所以对齐不是问题。
来自 malloc
的手册页:
For calloc()
and malloc()
, the value returned is a pointer to the
allocated memory, which is suitably aligned for any kind of
variable, or NULL if the request fails.
但是,如果您想确保这不会成为问题,只需声明一个联合实例,而不是在任何需要联合的函数被调用的地方声明包含结构。
我想弄清楚你从 C99 中对齐变量的地役权中得到了什么:
Exception to strict aliasing rule in C from 6.5.2.3 Structure and union members
如果原始写入是通过指向如下所示对齐结构之一的指针完成的,是否可以让您全权投射到该联合?
#include <stdio.h>
#include <stdlib.h>
struct Foo { char t; int i; };
struct Bar { char t; float f; };
union FooBar {
struct Foo foo;
struct Bar bar;
};
void detector(union FooBar *foobar) {
if (((struct Foo*)foobar)->t == 'F')
printf("Foo %d\n", ((struct Foo*)foobar)->i);
else
printf("Bar %f\n", ((struct Bar*)foobar)->f);
}
int main() {
struct Foo *foo = (struct Foo*)malloc(sizeof(struct Foo));
struct Bar *bar = (struct Bar*)malloc(sizeof(struct Bar));
foo->t = 'F';
foo->i = 1020;
detector((union FooBar*)foo);
bar->t = 'B';
bar->f = 3.04;
detector((union FooBar*)bar);
return 0;
}
注意在第二次调用中,t
被写为 "bar's t" 但是为了辨别它属于哪种类型,检测器会读取它作为 "foo's t"
我对 C++ 的反应是,只有拥有 "allocated it as a FooBar union in the first place" 才能做到这一点。认为这是合法的对我来说是违反直觉的,但对于 C 中的动态分配,没有这样的事情。因此,如果您不能这样做,那么在此异常下,您究竟可以使用上述动态内存分配做什么?
如果 Foo
和 Bar
有不同的对齐方式,你不应该仅仅因为这个原因就这样做。 union
将具有两者中最大的对齐方式,而使用较小值的那个会给你一个未正确对齐的 union
。
你的代码不是别名规则的好例子,因为你这里基本上没有别名。但总的来说,在可能出现别名的情况下,转换为另一种类型总是不好的。您的编译器可能会假设代码看到两个(或更多)指针。如果它们是不同的类型(char
类型除外),编译器可以假定它们永远不会指向同一个对象。
如果你做了这样的事情:
struct Foo foo;
struct Bar bar;
...
detector((union FooBar*)&foo);
detector((union FooBar*)&bar);
然后你可能会遇到对齐问题,因为编译器可能会以一种可能无法正确对齐另一个的方式将这些结构中的每一个放在堆栈上。
但是因为在您的情况下您是为每个结构动态分配内存,所以对齐不是问题。
来自 malloc
的手册页:
For
calloc()
andmalloc()
, the value returned is a pointer to the allocated memory, which is suitably aligned for any kind of variable, or NULL if the request fails.
但是,如果您想确保这不会成为问题,只需声明一个联合实例,而不是在任何需要联合的函数被调用的地方声明包含结构。