thi*_*his 3 c struct pointers c11
假设使用c11编译代码并启用严格别名.
我不是在寻找一种不同的方法,我想关注这个具体的问题,如果它有效或者为什么不能.
(如果我无意中犯了一些无关的错误,请告诉我,我会修复它)
c11标准说:
6.2.5.28所有指向结构类型的指针应具有相同的表示和对齐要求.
6.7.2.1.6结构是由一系列成员组成的类型,其存储按有序顺序分配
这意味着结构A和B中指针大小和指针对齐方式相同.
#include <stdio.h>
#include <stdlib.h>
struct S1
{
int i ;
} ;
struct S2
{
float f ;
} ;
struct A
{
struct S1* p ;
} ;
struct B
{
struct S2* p ;
} ;
int main( void )
{
Run Code Online (Sandbox Code Playgroud)
结构A和B具有指向结构S1和S2的指针,结构A和B保证具有相同的大小和对齐.
我们有一个struct B成员指针是struct S2指针,但指向一些struct S1,它使用void*cast实现.
struct S1 s1 = { 0 } ;
struct B* b = malloc( sizeof( *b ) ) ;
b->p = ( void* ) &s1 ;
Run Code Online (Sandbox Code Playgroud)
没关系,我们可以存储指针,只要我们实际上不使用指针.但是我们想要.我们可以将指针强制转换为struct S1.
( ( struct S1* )(b->p) )->i = 123 ; //redundant brackets for emphasis
printf("%d\n" , s1.i ) ;
Run Code Online (Sandbox Code Playgroud)
并正确使用它.
到目前为止,我没有看到任何问题,因为指针被转换为正确的类型.
但是我们可以将整个结构B转换为结构A吗?它们在大小和对齐方面是相同的,虽然标准可能会抱怨(?),编译器是否会产生未定义的行为?
( ( struct A* )b)->p->i = 666 ;
printf("%d\n" , s1.i ) ;
Run Code Online (Sandbox Code Playgroud)
我知道解决方案是使用union(或使用void并随时正确地转换),因为标准允许使用最后用于存储值的成员.
6.5.2.3.3(95)如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为6.2.6中描述的新类型中的对象表示(有时称为"类型双关"的过程).这可能是陷阱表示.
但是,我想避免这种情况:
struct C
{
union
{
struct S1* p1 ;
struct S2* p2 ;
} ;
} ;
struct C* c = malloc( sizeof( *c ) ) ;
c->p2 = ( void* )&s1 ;
c->p1->i = 444 ;
printf("%d\n" , s1.i ) ;
return 0 ;
}
Run Code Online (Sandbox Code Playgroud)
小智 6
你在此之前所描述的内容:
但是我们可以将整个结构B转换为结构A吗?
这一切都是正确的,但不幸的是,这个问题的答案是否定的.如果两个结构包含"公共初始序列",即它们的前几个成员具有相同类型,则只允许通过指向不兼容类型的指针访问结构.由于你的结构没有(即,第一个成员是不同类型的),S1通过指针访问类型的对象是不合法的,S2反之亦然.特别是,这样做违反了严格的别名规则.
从C99,6.5.7:
对象的存储值只能由具有以下类型之一的左值表达式访问:76)
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 对应于对象的有效类型的有符号或无符号类型,
- 对应于对象有效类型的限定版本的有符号或无符号类型,
- 聚合或联合类型,其成员中包含上述类型之一(包括递归地,子聚合或包含联合的成员),或者
- 角色类型.