是否允许使用指向某种类型的指针作为指向不同类型的指针,如果它们具有其他相同的指针成员仅在指向值的常量中不同?
具体而言,给出以下两种结构:
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);
}
我的总体动机是拥有一个foo
和const_foo
结构,它们的区别仅在于它们包含的嵌入指针所指向的对象的常量。对于任何不修改指向数据的函数,我想编写一个函数(采用const_foo
变量)并使其适用于两种类型的对象。
1 显然在后一种情况下(使用const_type
作为type
如果导致尝试修改指向的值,则不安全它们被定义为const
,但您可能认为这不会发生。)
答案 0 :(得分:2)
不,行为不是由C标准定义的。 (虽然它在各种意义上都是“允许的”,包括C实现可以支持这作为扩展。)
首先,C 2011 6.7.3 10说“要兼容两种合格类型,两者都应具有相同类型的兼容类型......”因此const char
和char
不兼容。
然后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->x
和b->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);
}