C中CVR类型资格的规则和解释的解释

An explanation on rules and interpretations of CVR type qualifications in C

我正在浏览 cppreference 上的 const, volatile and restrict 类型限定符页面。我对那里给出的 explanation/examples 有很多疑问和困惑。

  1. 这是那里给出的一个例子:
char *p = 0;
const char *pp = p;  // OK

char **q = 0;
const char **qq = q;  // ERROR

对此的解释是

for two types to be compatible, their qualifications must be identical.

但如果底层有不相容的条件,那么顶层如何兼容? C页和V页都有类似的说法。

  1. 两者有什么区别:
typedef int A[5];
const A x = {1, 2, 3, 4, 5};

const int x[5] = {1, 2, 3, 4, 5};

当我运行他们时,我没有看到任何区别。但是页面上说,

If an array type is declared with the const type qualifier (through the use of typedef), the array type is not const-qualified, but its element type is. (until C23)

An array type and its element type are always considered to be identically const-qualified. (since C23)

我没看懂这两个语句是什么意思!所有C、V和R页面都有类似的陈述。

  1. 这是一个错误吗?
typedef int A[2][3];
const A x = {{4, 5, 6}, {7, 8, 9}};
void *unqual_ptr = x;

那个页面说这在 C23 之前是可以的。但是 gcc 11.2 在 std=C89 本身给我错误!

  1. 这种方式使用的资格是什么意思?
void f(int m, int n, float a[restrict m][n], float b[restrict m][n]);

如能很好地解释如何 read/understand 并编写如此复杂的限定条件,我们将不胜感激。

问题 1

for two types to be compatible, their qualifications must be identical.

但是如果底部有不兼容的条件,那么顶部如何兼容?

char *const char * 是不兼容的类型。 char *p = 0; const char *pp = p; 是允许的,因为初始化允许将指向 non-qualified 类型的指针分配给指向无需限定符即可兼容的限定类型的指针。

初始化从赋值规则继承其规则,本例具体写法在6.5.16.1 1:

… the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;…

“左操作数在左值转换后的类型”是类型的非限定版本,根据 6.3.2.1 2:

… an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue;…

pp指向的类型是const char。左值转换后,这将是 char。而p指向charchar当然兼容char。此外,pp 指向的类型具有 p 指向的类型的所有限定符,因此允许此初始化。

char **q = 0; const char **qq = q;中,qq指向const char *。左值转换后,它仍然是 const char *,因为左值转换会删除指针的限定符,但不会删除所指向类型的限定符。所以这与 q 指向的类型 char *.

不兼容

问题 2

typedef int A[5];
const A x = {1, 2, 3, 4, 5};

const int x[5] = {1, 2, 3, 4, 5};

When I ran them, I did not see any difference.

您没有展示任何可能检测到它们之间差异的实验,因此很难发表评论。

But the page says,

If an array type is declared with the const type qualifier (through the > use of typedef), the array type is not const-qualified, but its element type > is. (until C23)

这表示在const A x;中,const从数组转移到它的元素,所以x的类型是“array of 5 const int".

An array type and its element type are always considered to be identically const-qualified. (since C23)

这表示预计在 2023 年即将发布的 C 标准版本中会发生变化,这将使 x 的类型成为“const 数组 5 const int”。

问题 3

typedef int A[2][3];
const A x = {{4, 5, 6}, {7, 8, 9}};
void *unqual_ptr = x;

That page says this is OK till C23. But gcc 11.2 is giving me error in std=C89 itself!

页面似乎有误。 C 2018 6.5.16.1 1 说:

… the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;…

这种情况不允许 void *unqual_ptr = x;,因为左操作数没有右操作数所具有的 const 限定符。该段中的其他情况 none 将适用。

问题 4

What is the meaning of qualifications used in this way?

void f(int m, int n, float a[restrict m][n], float b[restrict m][n]);

在函数参数的声明中,限定符可以出现在 top-level 数组声明符的 [] 中。声明为数组的函数参数会自动调整为指针。 [] 内的任何限定符然后应用于指针而不是数组或其元素类型。因此 float a[restrict m][n]a 声明为 restrict 限定的指向 n float 数组的指针,就好像它已被声明为 float (* restrict a)[n] 一样。