兼容的类型和忽略C类系统中的顶级限定符 [英] Compatible types and ignoring top-level qualifiers in the C type system

查看:116
本文介绍了兼容的类型和忽略C类系统中的顶级限定符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个多部分的问题。



我一直在试图理解C型系统。首先C标准
提到了术语兼容类型很多,所以我试图理解这一点。
该定义似乎相当分散,但是从我发现的内容来看:


6.2.7兼容类型和复合类型1如果类型相同,则两种类型具有兼容类型。确定
是否有两种类型兼容的附加规则在类型
说明符中的6.7.2中描述,类型限定符在6.7.3中描述,而在
声明符中描述为6.7​​.6。 ,如果它们的标签
和成员满足以下要求,则在单独的翻译单元中声明的两个结构体,联合体或枚举类型
是兼容的:如果其中一个被标记为
,则另一个应该是用相同的标签声明。如果两者都是在其各自翻译单位内的任何地方完成的话,那么符合附加要求的
适用:它们的成员之间应该有一对一的
对应关系,从而每对
对应的成员声明为兼容类型;如果该对的
成员中有一个用对齐说明符声明,则另一个
用等价的对齐说明符声明;如果这对货币中的一个成员
用名称声明,另一个用
声明同名。对于两种结构,相应的成员应按同一顺序申报
。对于两个结构或联合,相应的
位域应具有相同的宽度。对于两个枚举,
对应的成员应该具有相同的值。




  REFS: 
6.7.2 short == short int == signed short == signed short int等
6.7.3
10)对于两个需要兼容的限定类型,两者必须具有相同的兼容类型的合格版本;说明符或限定符列表中的类型限定符的顺序不会影响指定的类型。
6.7.6
1.2)
对于两个要兼容的指针类型,两者应具有相同的限定条件,并且两者都应指向兼容类型。
2.6)
对于两个要兼容的数组类型,两者都应具有兼容的元素类型,如果两个大小说明符都存在,并且是整型常量表达式,则两个大小说明符都应具有相同的常量值。如果两个数组类型在需要它们兼容的上下文中使用,那么如果两个大小说明符求值为不等值,则它是未定义的行为。

在我看来,


  1. 如果它们的所有完整部分相同,则两个类型是相同的。

  2. (作为1的结果)。完全兼容的类型有效意思是相同类型。

首先,我想问一下我的解释是否准确。



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


6.5.1.1通用选择2通用选择不得有一个以上的默认通用关联。通用
关联中的类型名称应指定一个完全不同于
修饰类型的对象类型。同一个通用
选择中的两个通用关联不应指定兼容类型。通用选择的控制表达式
应该具有与其通用关联列表中命名的类型中最多一个兼容的类型。如果通用
选择没有默认的通用关联,则其控制
表达式的类型应与其通用关联列表中命名的类型
中的一种类型兼容。


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

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

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

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

表示int甚至在叮当声。



所以我的第二个问题是标准中的内容是解释(int const)0 和类型(int const){0} 类型的$ c> int const



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

$ b

 用于tcc gcc clang中的CC;做回声CC = $ CC; $ CC -x c  - <<<'int main(){static void(* f)(int *),(* g)(int * restrict const volatile); F =克; }';完成#无投诉

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

在确定函数兼容性的背景下,忽略了原型类型列表中的顶级限定符吗?



谢谢。

解决方案

(const int){0}



我也认为clang是正确的;这有一个不同于(int){0} 的类型。见下面。



2。 (const int)0



首先,请注意:


与限定类型关联的属性仅对于左值的表达式有意义。 (&#; 6.7.3(类型限定符)/第4段)

结果不是一个左值,注意104到&n; 6.5.4(演员操作员):


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


脚注不具有约束力,但是这个脚注来源于这样一个事实,即演员操作符不在左值转换的例外列表中(参见6.3.2.1节),因此演员会导致左值转换结果是限定符被删除:


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

与第一种情况的区别是挺清楚的。第一种情况是一种复合文字,而不是一种强制转换,并且:


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

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



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

请参阅&#; 6.7.6.3(函数声明符)/第15段的最后一句:


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



This is a multi-part question.

I've been trying to understand the C type system. First the C standard mentions the term "compatible type" a lot so I tried to understand that. The definition seems to be quite spread out, but from what I found:

6.2.7 Compatible type and composite type 1 Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators.55) Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are completed anywhere within their respective translation units, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types; if one member of the pair is declared with an alignment specifier, the other is declared with an equivalent alignment specifier; and if one member of the pair is declared with a name, the other is declared with the same name. For two structures, corresponding members shall be declared in the same order. For two structures or unions, corresponding bit-fields shall have the same widths. For two enumerations, corresponding members shall have the same values.

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.

it appears to me that

  1. Two types are compatible if they're the same if all their complete parts are the same.
  2. (as a consequence of 1.) "Complete compatible types" effectively means "same types".

So first, I'd like to ask if my interpretation is accurate.

Second, _Generic selections in the standard are defined in terms of this notion of "compatible type":

6.5.1.1 Generic selection 2 A generic selection shall have no more than one default generic association. The type name in a generic association shall specify a complete object type other than a variably modified type. No two generic associations in the same generic selection shall specify compatible types. The controlling expression of a generic selection shall have type compatible with at most one of the types named in its generic association list. If a generic selection has no default generic association, its controlling expression shall have type compatible with exactly one of the types named in its generic association list.

but compilers appear to interpret it differently in regards to top level qualifiers:

$ $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

It seems to me the clang interpretation is correct, however what's perplexing is that

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

says "int" even on clang.

So my second question is what in the standard would be the basis for interpreting (int const)0 as of type int and (int const){0} as of type int const?

Finally, in all of my compilers (tcc,gcc,clang) top level qualifiers appear to be ignored on all types in prototype type lists when determining compatibility between fucntions or function pointers:

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

but I couldn't find any mention of this in the standard, so my final question is:

Is ignoring top level qualifiers on types in prototype type lists standard-sactioned in the context of determining function-compatibility?

Thanks.

解决方案

1. (const int){0}

I also think that clang is correct; this has a different type than (int){0}. See below.

2. (const int)0

First, note that:

The properties associated with qualified types are meaningful only for expressions that are lvalues. (§6.7.3 (Type qualifiers) / para 4)

The result of a cast is not an lvalue, as indicated by note 104 to §6.5.4 (Cast operators):

A cast does not yield an lvalue. Thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type.

Footnotes are not binding, but this footnote derives from the fact that the cast operator is not in the list of exceptions to lvalue conversion (see §6.3.2.1), so the cast induces lvalue conversion with the result that qualifiers are deleted:

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; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. (§6.3.2.1 (Lvalues, arrays, and function designators) / para. 2)

The difference with the first case is quite clear. The first case is a compound literal, not a cast, and:

… (when the type name specifies an object type), the type of the compound literal is that specified by the type name. … the result is an lvalue. (§6.5.2.5 (Compound literals) / para 4)

Since the compound literal is an lvalue, it should retain its const qualifier, and that means that gcc is wrong in the first example.

3. Qualified function parameters and type compatibility

See the last sentence of §6.7.6.3 (Function declarators) / para 15:

In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.

这篇关于兼容的类型和忽略C类系统中的顶级限定符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆