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

时间:2016-09-16 17:50:03

标签: c pointers undefined-behavior unions strict-aliasing

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。那么,我担心的是代码是否会被破坏,例如违反严格的别名规则,未定义的行为等等?

3 个答案:

答案 0 :(得分:1)

该行:

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

将结构的成员ch的地址分配给union的成员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))
如果用于读取union对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的相应部分被重新解释 作为6.2.6中描述的新类型中的对象表示(有时称为''类型的过程) 双关语””)。这可能是陷阱表示。

答案 1 :(得分:0)

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

所以在这些实现上,它应该像你期望的那样工作。

答案 2 :(得分:0)

C标准试图定义所有符合要求的实现所需的特征,特征和保证;它不会努力描述99%的实现中常见的大多数有用的特性和功能,但这些特性和功能并不是必需的。

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

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

但由于C标准不要求编译器识别别名 函数传递除void *之外的任何指针的地址, 许多现代编译器不再支持此类构造,除非所有类型都基于类型 别名优化被禁用。