不同类型和大小的指针的联合

Union of different types and sizes of pointers

typedef struct  {  char   ch;  int   num;  }  st_t;
typedef union   {  char *pch;  st_t *pst;  }  un_t;

st_t st;
st.ch = 's';

un_t un = { &st.ch };
*un.pch = 'u';

printf("%c\n", un.pst->ch);  // expect: print the letter 'u'

据我所知,结构体第一个成员的地址和结构体本身的地址是一样的,所以un可以同时指向stst.ch 同时访问 pstpch。但是,C99 标准似乎从未明确表示 the sizes of different types of pointers are identical。然后,我担心代码会不会被破坏,例如违反严格的别名规则,成为未定义的行为等?

虽然这样的构造并不严​​格符合指针大小的标准,但实际上最常见的实现(即 msvc,gcc/linux)确实对非函数指针使用相同的表示形式。

因此,在这些实现中,它应该可以如您所愿地工作。

行:

un_t un = {&st.ch};
*un.pch = 'u';

将结构的成员 ch 的地址分配给联合的成员 pch,然后使用该指针向该地址写入一个字符。这是完全正确的。

后面一行有问题:

printf("%c\n", un.pst->ch);

除了上次存储的成员之外的联合成员被读取。
下面可能是陷阱1表示2:

un.pst

1(引自:ISO/IEC 9899:201x 6.2.6.1 总则 5)
某些对象表示不需要表示对象类型的值。如果存储 对象的值具有这样的表示,并由执行以下操作的左值表达式读取 没有字符类型,行为未定义。如果产生这样的表示 通过一个通过左值表达式修改对象的全部或任何部分的副作用 没有字符类型,行为未定义。 50)这样的表示被称为 陷阱表示。

2(引自:ISO/IEC 9899:201x 6.5.2.3 结构和联合成员 3 脚注 95)
如果用于读取联合对象内容的成员与上次用于读取的成员不同 在对象中存储一个值,该值的对象表示的适当部分被重新解释 作为 6.2.6 中描述的新类型的对象表示(有时称为“类型”的过程 双关语”)。这可能是陷阱表示。

C 标准试图定义所有符合要求的实现所需的特性、特性和保证;它不会努力描述 99% 的实现所共有的大部分有用特性和特性,但这些特性不是必需的。

在绝大多数实现中,所有数据指针都使用相同的表示,因此给定 thingType *foo=xxx; void *vfoo = foo;,两者都 foovfoo 将保持相同的位模式。在咄咄逼人的日子之前 优化器,这样的实现将非常有用地允许任何类型的数据 使用 void** 访问指针,因此可以编写如下函数:

int realloc_if_possible(void **p, int newsize)
{
  void *temp = realloc(*p, newsize);
  if (!temp) return -1;
  *p = temp;
  return 0;
}

但由于 C 标准不要求编译器在以下情况下识别别名 向函数传递除 void* 以外的任何类型指针的地址, 许多现代编译器不再支持这种结构,除非所有基于类型的 禁用别名优化。