我的程序(https://godbolt.org/z/Y93eG7K7s):
int main(){
int temp = 0;
int* tempp = &temp;
int** temppp = &tempp;
int*** tempppp = &temppp;
const int* intp0;
intp0 = tempp; // A
const int** intp1;
intp1 = temppp; // B
}
GCC 或 Clang 都可以编译,但两者都会在 B 行中引发相同的“不兼容指针类型”警告。我对这个警告没有问题,因为
const int **
和 int **
绝对是两个 in 兼容的指针类型。然而(在我看来),const int *
和int *
也是两个in兼容的指针类型(A行)。
因此我的问题是:为什么
const int *
和int *
被认为是兼容的指针类型?
GCC 警告消息措辞不正确;
intp1 = temppp
违反的规则不是=
的操作数必须兼容,而是它们必须符合某些约束。兼容性是这些限制中的因素之一,但它不是这里讨论的因素,因此该消息具有误导性。尽管问题指出 Clang 提出了“不兼容的指针类型”警告,但我没有看到这一点; Compiler Explorer 上可用的每个版本的 Clang 都会报告正确的错误消息,“从 'int **' 分配给 'const int **' 会丢弃嵌套指针类型中的限定符。”
int *
可以分配给 const int *
,因为简单分配的规则允许向直接指向的类型添加限定符。 C 2018 6.5.16.1 说:
应满足以下条件之一:
…
—左操作数具有原子、限定或非限定指针类型,并且(考虑左值转换后左操作数将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,以及左操作数指向的类型具有右边指向的类型的所有限定符; …
const int *
指向的不合格类型是int
,int *
指向的不合格类型也是int
,并且int
与自身兼容。此外, const int *
指向的类型 const int
具有 int *
指向的类型 int
的所有限定符。
相反,
const int **
指向的不合格类型是const int *
,int **
指向的不合格类型是int *
,并且const int *
与int *
不兼容。 (请注意,虽然 const int *
指向限定类型,但它本身是不限定的;const int *
类型的对象可以更改为指向不同的 const int
;它不是 const
限定的。)所以 intp1 = temppp
不满足此约束,因此编译器会发出警告。
不允许这样做的原因是它可能导致指向限定类型的指针指向没有该限定符的对象。 C 2018 6.5.16.1 6 给出了一个示例,此处稍加修改并附有我的评论:
const int **ipp;
int *p;
const int i = 1;
/* The next line violates the constraints because `&p` is an `int **`, and
it is assigned to a `const int **`.
For illustration, suppose we suspend the constraint and allow it.
*/
ipp = &p;
/* In the following, both `*ipp` and `&i` are `const int *`, so this is
an ordinary assignment of identical types.
*/
*ipp = &i;
/* In the following, `*p` is an `int`, to which an `int` is assigned. So
this is an ordinary assignment of identical types. However, `p` points
to `i`, which is `const`, so this assignment would change the value of
a `const` object. The assignment `ipp = &p` above enabled this, so it
is unsafe and should be disallowed.
*/
*p = 0;
因此我的问题是:为什么
和const int *
被认为是兼容的指针类型?int *
它们不是兼容的指针类型。如上所述,不允许赋值的原因是有关限定符的规则,而不是有关兼容类型的规则。
C 中兼容类型的概念不是关于一种类型是否可以分配给另一种类型,而是关于两种类型除了我们不知道的部分之外是否实际上相同。例如,三个
int
的数组和未指定数量的 int
的数组是兼容的——我们所知道的关于这两种类型的所有部分都是相同的。如果未指定的部分完成,它们可能是相同的。同样,返回带有未知参数的 void *
的函数与返回带有某些声明参数的 void *
的函数兼容。这个兼容性问题与作业中的限定符问题无关。