具有不同const限定指针的结构之间的兼容性

时间:2017-12-08 00:03:12

标签: c language-lawyer c11 type-punning

是否允许使用指向某种类型的指针作为指向不同类型的指针,如果它们具有其他相同的指针成员仅在指向值的常量中不同?

具体而言,给出以下两种结构:

struct type {
  char *a;
   int *b;
};

struct const_type {
  const char *a;
  const  int *b;
};

是否允许将指向type的指针视为指向const_type,反之亦然 1 ,例如将type指针传递给函数期望const_type指针如图所示:

int add(const const_type* t2) {
  return *t2->a + *t2->b;
}

int is_it_legal() {
  int some_int = 42;
  char blah[] = "six times and counting...";
  type t1 = {blah, &some_int};
  return add((const_type*)&t1);
}

我的总体动机是拥有一个fooconst_foo结构,它们的区别仅在于它们包含的嵌入指针所指向的对象的常量。对于任何不修改指向数据的函数,我想编写一个函数(采用const_foo变量)并使其适用于两种类型的对象。

1 显然在后一种情况下(使用const_type作为type如果导致尝试修改指向的值,则不安全它们被定义为const,但您可能认为这不会发生。)

3 个答案:

答案 0 :(得分:2)

不,行为不是由C标准定义的。 (虽然它在各种意义上都是“允许的”,包括C实现可以支持这作为扩展。)

首先,C 2011 6.7.3 10说“要兼容两种合格类型,两者都应具有相同类型的兼容类型......”因此const charchar不兼容。

然后6.7.6.1说“要使两个指针类型兼容,两者都应具有相同的限定条件,并且两者都应成为兼容类型的指针。”因此const char *char *不兼容。

接下来,6.2.7 1说“此外,如果他们的标签和成员满足以下要求,则在单独的翻译单元中声明的两种结构,联合或枚举类型是兼容的:......如果两者都在各自的翻译单元内的任何地方完成,那么以下附加要求适用:它们的成员之间应该有一对一的对应关系,以便每对相应的成员都被声明为兼容的类型; ......“此外,它还要求”如果一个声明带有标签,另一个应使用相同的标签声明。“因此结构不兼容。

最后,6.5 7告诉我们“对象的存储值只能由具有以下类型之一的左值表达式访问:与对象的有效类型兼容的类型,与之兼容的类型的限定版本对象的有效类型,......“第一个不适用,因为结构不兼容。第二个说你可以引用一个使用const版本的结构,遗憾的是不适用,因为这里的const限定了成员,而不是结构。

答案 1 :(得分:2)

如果两个结构具有完全相同的成员列表,则甚至不能保证工作。该代码是严格的别名违规行为,通过type1类型的左值访问type2

与严格的别名讨论一样:规则的文字没有明确规定,但是被广泛接受的x->y被定义为(*x).y,被视为{{*x的访问权限。 1}}用于严格别名的目的。这个代码的基本原理可以在这段代码中看到:

void f(struct A *a, struct B *b)
{
     a->x = 5;
     b->y = 6;
}

编译器可以自由地假设a->xb->y不能相互别名,即使它们碰巧都有类型int,也不管它们在结构中的偏移量。

但是,如果声明包含两个结构类型的联合,并且该声明在尝试别名时可见,则会绕过严格别名规则,即使对象是别名目前还没有加盟!

See this thread有关该主题的良好报道,并对编译器合规性进行了评论。

回到你的问题:因为你的结构甚至不具有相同的成员列表,6.5.2.3 / 6中的文本关于联合中的常见初始序列:

  

如果相应的成员,两个结构共享一个共同的初始序列   对于一个或多个初始成员的序列具有兼容类型(以及对于位字段,具有相同的宽度)。

不适用;这两种类型只是不相关的结构类型。 ("兼容类型"的定义在6.2.7中 - 更改限定符使类型不兼容)。将它们放入工会并没有任何收获。

最后,正如链接线程所讨论的那样; 标准所说的编译器实际上做什么在这个领域不是一回事。

答案 2 :(得分:1)

IMO,我认为const在您想要使用它的方式上并不重要。 另外,我认为你的意思是:

typedef struct type1 {
  char *a;
   int *b;
}type1;

typedef struct type2 {
  const char *a;
  const  int *b;
}type2;


int add(const type2* t2) {
  return t2->a[0] + *t2->b;
}

int is_it_legal() {

  int one = 1;    

  type1 t1 = {"third time the charm?", &one};
  return add((type2*)&t1);
}