兼容类型并忽略C类型系统中的顶级限定符

时间:2018-03-25 18:37:19

标签: c generics language-lawyer type-systems

这是一个多部分问题。

我一直试图了解C型系统。首先是C标准 提到术语"兼容类型"很多,所以我试着理解这一点。 这个定义似乎很分散,但我发现:

  

6.2.7兼容类型和复合类型1如果类型相同,则两种类型具有兼容类型。确定的附加规则   6.7.2中是否描述了两种类型是否兼容的类型   说明符,在6.7.3中用于类型限定符,在6.7.6中用于   声明符.55)此外,有两种结构,联合或枚举类型   在单独的翻译单元中声明的标签是兼容的   并且成员满足以下要求:如果声明了一个   使用标记,另一个应使用相同的标记声明。如果两者都是   在各自的翻译单位内完成,然后是   以下附加要求适用:应一对一   他们的成员之间的对应,使每对   相应的成员用兼容的类型声明;如果一个   该对的成员用对齐说明符声明,另一个   声明具有等效的对齐说明符;如果是一个成员   该对用一个名称声明,另一个用声明   一样的名字。对于两个结构,应声明相应的成员   以相同的顺序。对于两个结构或联合,对应   位域应具有相同的宽度。对于两个枚举,   相应的成员应具有相同的值。

REFS:
    6.7.2  short == short int == signed short == signed short int, etc.
    6.7.3
        10) For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.
    6.7.6
        1.2)
            For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
        2.6)
For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.

在我看来

  1. 如果两种类型的所有完整部分相同,则它们是相容的。
  2. (由于1.)"完全兼容的类型"有效地意味着"相同类型"。
  3. 首先,我想问一下我的解释是否准确。

    其次,标准中的_Generic选项是根据"兼容类型"的定义来定义的:

      

    6.5.1.1通用选择2通用选择应具有不超过一个默认通用关联。泛型中的类型名称   关联应指定一个完整的对象类型而不是可变的   修改类型。同一个泛型中没有两个通用关联   选择应指定兼容类型。控制表达   通用选择的类型应至少与其中一个类型兼容   在其通用关联列表中命名的类型。如果是通用的   选择没有默认的通用关联,它的控制   表达式的类型应与其中一种类型兼容   在其通用关联列表中命名。

    但编译器似乎对顶级限定符的解释不同:

    $ $CC -x c -include stdio.h - <<<'int main(){puts( _Generic((int const){0}, int:"int", int const: "int const")); }' && ./a.out      #int with gcc, and int const with clang
    

    在我看来,铿锵的解释是正确的,然而令人困惑的是

    $ $CC -x c -include stdio.h - <<<'int main(){puts( _Generic((int const)0, int:"int", int const: "int const")); }' && ./a.out        
    

    即使在clang上也说"int"

    所以我的第二个问题是标准中的内容是从类型(int const)0解释int和从(int const){0}类型解释int const的基础?

    最后,在我的所有编译器(tcc,gcc,clang)中,顶级限定符似乎在原型类型列表中的所有类型上被忽略 在确定fucntions或函数指针之间的兼容性时:

    for CC in tcc gcc clang; do echo CC=$CC; $CC -x c  - <<<'int main(){ static void (*f)(int*), (*g)(int * restrict const volatile);  f=g; }' ; done #no complaints
    

    但我在标准中找不到任何提及,所以我的最后一个问题是:

    在确定函数兼容性的上下文中,是否忽略了原型类型中的类型的顶级限定符列出了标准语言?

    感谢。

3 个答案:

答案 0 :(得分:2)

事情稍微复杂一点,因为_Generic()有一些额外的规则,并且因为您似乎在clang中遇到了已被修复的错误。

C18(6.5.1.1第2部分)为通用选择添加了一些要求:

  

控制表达式的类型是表达式的类型,就好像它已经进行了左值转换,93)数组到指针转换或函数到指针转换一样。

脚注93的状态:

  

左值转换会丢弃类型限定符。

以您的_Generic((int const){0}, ...)为例,FreeBSD clang版本6.0.1报告int

基本上,由于控制表达式的左值转换,泛型选择不是探索兼容类型概念的好方法,因为它与类型限定符有关。

答案 1 :(得分:1)

1。 (const int){0}

我也认为铿锵是正确的;这与(int){0}的类型不同。见下文。

2。 (const int)0

首先,请注意:

  

与限定类型关联的属性仅对作为左值的表达式有意义。 (§6.7.3(类型限定词)/第4段)

演员的结果不是左值,如§6.5.4的注释104(演员)所示:

  

演员阵容不会产生左值。因此,对合格类型的强制转换与对该类型的非限定版本的强制转换具有相同的效果。

脚注没有约束力,但是这个脚注来源于强制转换运算符不在左值转换的异常列表中的事实(参见§6.3.2.1),因此强制转换会导致左值转换,结果是删除了限定词:

  

没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换。如果左值具有限定类型,则该值具有左值类型的非限定版本;另外,如果左值具有原子类型,则该值具有左值类型的非原子版本;否则,该值具有左值的类型。 (§6.3.2.1(左值,数组和函数指示符)/第2段)

与第一种情况的区别非常明显。第一种情况是复合文字,而不是强制转换,并且:

  

...(当类型名称指定对象类型时),类型   复合文字的大小是由类型名称指定的。 ......结果是左值。 (§6.5.2.5(复合文字)/第4段)

由于复合文字是左值,它应该保留其const限定符,这意味着第一个例子中gcc是错误的。

3。合格的功能参数和类型兼容性

见§6.7.6.3(函数声明者)/第15段的最后一句:

  

在确定类型兼容性和复合类型时,使用函数或数组类型声明的每个参数都被视为具有调整类型,并且使用限定类型声明的每个参数都被视为具有其声明类型的非限定版本。 / p>

答案 2 :(得分:1)

兼容类型并不意味着它们必须所有用途都完全相同。通知

struct foo *

与一个翻译单元兼容

struct foo *

如果都不是指向完整类型的指针。但是,如果foo的成员即使在声明之后也被定义,则定义必须匹配,否则先前的指针将不兼容!

同样,一个类型及其typedef彼此兼容。

但是兼容性不需要类型相同:数组可以具有不完整和完整的类型,并且彼此兼容。您可以在一个翻译单元中声明

extern int a[];

和另一个

int a[10];

它们是兼容的类型。

C11 6.7.6p6

  

要使两个数组类型兼容,它们都应具有兼容的元素类型,并且如果同时存在两个大小说明符,并且它们是整数常量表达式,则两个大小说明符都应具有相同的常量值。如果在要求它们兼容的上下文中使用这两种数组类型,则如果两个大小说明符的计算结果不相等,则这是未定义的行为。

此外,VLA类型可以是与静态维数组兼容的类型-如果元素类型相同,则始终被认为是兼容的,但是如果尺寸实际上不匹配,则行为将不确定必填。


对于_Generic,Clang在这里肯定是有错的。实际上,Defect report 481中已解决了这一问题,并且认为Clang始终是错误的,而GCC是正确的;该标准已针对C18 as noted by ov2k进行了修订。另请参阅this Q/A,这是由于Clang没有将数组左值转换为指针类型而引起的。

(const int){0}确实创建了类型为const int(复合文字)的左值,但随后的左值转换应删除类型限定符,结果应为int。实际上,_Generic选择不能选择const限定的任何类型,因此我认为compilers should issue a warning for even having the const qualifier there