类型
(另请参见 算术类型 以了解有关大多数内置类型的详细信息以及 C 库提供的类型相关实用程序列表。)
对象、函数 和 表达式 具有一个名为类型的属性,它决定了存储在对象中的二进制值的解释方式或表达式求值的结果。
内容 |
[编辑] 类型分类
C 类型系统包含以下类型
- 类型 void
- 基本类型
- 类型 char
- 带符号整数类型
- 标准:signed char, short, int, long, long long(自 C99)
|
(自 C23) |
|
(自 C99) |
- 无符号整数类型
- 标准:_Bool,(自 C99) unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long(自 C99)
|
(自 C23) |
|
(自 C99) |
- 浮点类型
- 实浮点类型:float, double, long double
|
(自 C23) |
|
(自 C99) |
- 派生类型
(自 C11) |
对于上面列出的每种类型,可能存在几种类型的限定版本,对应于 const
、volatile
和 restrict
限定符(在限定符的语义允许的情况下)的组合。
[编辑] 类型组
- 对象类型:所有不是函数类型的类型
- 字符类型:char, signed char, unsigned char
- 整数类型:char, 带符号整数类型,无符号整数类型,枚举类型
- 实数类型:整数类型和实浮点类型
- 算术类型:整数类型和浮点类型
- 标量类型:算术类型,指针类型和 nullptr_t(自 C23)
- 聚合类型:数组类型和结构类型
- 派生声明符类型:数组类型,函数类型和指针类型
构造一个完整的对象类型,使其对象表示中的字节数在 size_t 类型(即 sizeof
运算符的结果类型)中不可表示,包括在运行时形成这样的 VLA 类型,(自 C99) 是未定义的行为。
[编辑] 兼容类型
在 C 程序中,引用不同翻译单元中相同对象或函数的声明不必使用相同的类型。它们只需要使用足够相似的类型,正式称为兼容类型。同样适用于函数调用和左值访问;参数类型必须与参数类型兼容,左值表达式类型必须与被访问的对象类型兼容。
类型T
和U
是兼容的,如果
- 它们是相同的类型(相同的名称或由
typedef
引入的别名) - 它们是兼容的未限定类型的相同 cvr 限定版本
- 它们是指针类型,并且指向兼容的类型
- 它们是数组类型,并且
- 它们的元素类型兼容,并且
- 如果两者都具有固定大小,则该大小相同。注意:未知范围的数组与任何具有兼容元素类型的数组兼容。 VLA 与任何具有兼容元素类型的数组兼容。(自 C99)
- 它们都是结构/联合/枚举类型,并且
- (C99)如果一个用标签声明,另一个也必须用相同的标签声明。
- 如果两者都是完成类型,它们的成员必须在数量上完全对应,用兼容类型声明,并具有匹配的名称。
- 此外,如果它们是枚举,则对应的成员也必须具有相同的值。
- 此外,如果它们是结构或联合,
- 对应的成员必须以相同的顺序声明(仅结构)
- 对应的 位域 必须具有相同的宽度。
- 一个是枚举类型,另一个是该枚举的底层类型
- 它们是函数类型,并且
- 它们的返回类型兼容
- 它们都使用参数列表,参数的数量(包括使用省略号)相同,并且在应用数组到指针和函数到指针类型调整之后,并且在剥离顶层限定符之后,对应的参数具有兼容的类型
|
(直到 C23) |
类型 char 与 signed char 不兼容,也不与 unsigned char 兼容。
如果两个声明引用相同对象或函数,并且不使用兼容的类型,则程序的行为是未定义的。
// Translation Unit 1 struct S { int a; }; extern struct S *x; // compatible with TU2's x, but not with TU3's x // Translation Unit 2 struct S; extern struct S *x; // compatible with both x's // Translation Unit 3 struct S { float a; }; extern struct S *x; // compatible with TU2's x, but not with TU1's x // the behavior is undefined
// Translation Unit 1 #include <stdio.h> struct s { int i; }; // compatible with TU3's s, but not TU2's extern struct s x = {0}; // compatible with TU3's x extern void f(void); // compatible with TU2's f int main() { f(); return x.i; } // Translation Unit 2 struct s { float f; }; // compatible with TU4's s, but not TU1's s extern struct s y = {3.14}; // compatible with TU4's y void f() // compatible with TU1's f { return; } // Translation Unit 3 struct s { int i; }; // compatible with TU1's s, but not TU2's s extern struct s x; // compatible with TU1's x // Translation Unit 4 struct s { float f; }; // compatible with TU2's s, but not TU1's s extern struct s y; // compatible with TU2's y // the behavior is well-defined: only multiple declarations // of objects and functions must have compatible types, not the types themselves
注意:C++ 没有兼容类型的概念。一个 C 程序在不同的翻译单元中声明两个兼容但不相同的类型不是有效的 C++ 程序。
[编辑] 复合类型
复合类型可以由两个兼容的类型构成;它是一个与这两个类型都兼容的类型,并满足以下条件
- 如果两个类型都是数组类型,则应用以下规则
- 如果一个类型是已知常数大小的数组,则复合类型是该大小的数组。
|
(自 C99) |
- 否则,两个类型都是未知大小的数组,并且复合类型是未知大小的数组。
- 复合类型的元素类型是两个元素类型的复合类型。
|
(直到 C23) |
- 如果两个类型都是带有参数类型列表的函数类型,则复合参数类型列表中每个参数的类型是相应参数的复合类型。
这些规则递归地应用于派生出这两个类型的类型。
// Given the following two file scope declarations: int f(int (*)(), double (*)[3]); int f(int (*)(char *), double (*)[]); // C23: Error: conflicting types for 'f' // The resulting composite type for the function is: int f(int (*)(char *), double (*)[3]);
对于在可见先前声明该标识符的范围中声明的具有内部或外部链接的标识符,如果先前声明指定内部或外部链接,则标识符在稍后声明处的类型将变为复合类型。
[编辑] 不完整类型
不完整类型是缺乏足够信息来确定该类型对象大小的对象类型。不完整类型可以在翻译单元的某个时间点完成。
以下类型是不完整的
- void 类型。此类型无法完成。
- 未知大小的数组类型。它可以通过稍后声明来完成,该声明指定大小。
extern char a[]; // the type of a is incomplete (this typically appears in a header) char a[10]; // the type of a is now complete (this typically appears in a source file)
- 未知内容的结构或联合类型。它可以通过在相同范围内稍后声明的相同结构或联合来完成,该声明定义其内容。
struct node { struct node *next; // struct node is incomplete at this point }; // struct node is complete at this point
[编辑] 类型名称
类型可能需要在声明以外的上下文中命名。在这些情况下,使用类型名称,在语法上与类型说明符和类型限定符列表完全相同,后面跟着声明符(见声明),就像用于声明此类型的单个对象或函数一样,除了标识符被省略
int n; // declaration of an int sizeof(int); // use of type name int *a[3]; // declaration of an array of 3 pointers to int sizeof(int *[3]); // use of type name int (*p)[3]; // declaration of a pointer to array of 3 int sizeof(int (*)[3]); // use of type name int (*a)[*] // declaration of pointer to VLA (in a function parameter) sizeof(int (*)[*]) // use of type name (in a function parameter) int *f(void); // declaration of function sizeof(int *(void)); // use of type name int (*p)(void); // declaration of pointer to function sizeof(int (*)(void)); // use of type name int (*const a[])(unsigned int, ...) = {0}; // array of pointers to functions sizeof(int (*const [])(unsigned int, ...)); // use of type name
除了标识符周围的冗余括号在类型名称中是有意义的,并且代表“没有参数规范的函数”
int (n); // declares n of type int sizeof(int ()); // uses type "function returning int"
类型名称用于以下情况
(自 C99) | |
(自 C11) |
类型名称可以引入一个新的类型
void* p = (void*)(struct X { int i; } *)0; // type name "struct X {int i;}*" used in the cast expression // introduces the new type "struct X" struct X x = {1}; // struct X is now in scope
[编辑] 参考文献
- C23 标准 (ISO/IEC 9899:2024)
- 6.2.5 类型 (p: TBD)
- 6.2.6 类型表示 (p: TBD)
- 6.2.7 兼容类型和复合类型 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018)
- 6.2.5 类型 (p: 31-33)
- 6.2.6 类型表示 (p: 31-35)
- 6.2.7 兼容类型和复合类型 (p: 35-36)
- C11 标准 (ISO/IEC 9899:2011)
- 6.2.5 类型 (p: 39-43)
- 6.2.6 类型表示 (p: 44-46)
- 6.2.7 兼容类型和复合类型 (p: 47-48)
- C99 标准 (ISO/IEC 9899:1999)
- 6.2.5 类型 (p: 33-37)
- 6.2.6 类型表示 (p: 37-40)
- 6.2.7 兼容类型和复合类型 (p: 40-41)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.1.2.5 类型
- 3.1.2.6 兼容类型和复合类型
[编辑] 另见
C++ 文档 用于 类型
|