类型
(另请参阅 算术类型,了解大多数内置类型的详细信息,以及 C 库提供的与类型相关的实用工具列表。)
对象、函数 和 表达式 具有名为类型的属性,该属性决定了存储在对象中或由表达式求值的二进制值的解释。
目录 |
[编辑] 类型分类
C 类型系统由以下类型组成
- 类型 void
- 基本类型
- 类型 char
- 有符号整数类型
- 标准:signed char、short、int、long、long long(since C99)
|
(since C23) |
|
(since C99) |
- 无符号整数类型
- 标准:_Bool,(since C99) unsigned char、unsigned short、unsigned int、unsigned long、unsigned long long(since C99)
|
(since C23) |
|
(since C99) |
- 浮点类型
- 实浮点类型:float、double、long double
|
(since C23) |
|
(since C99) |
- 派生类型
(since C11) |
对于上面列出的每种类型,可能存在其类型的多个限定版本,对应于 const
、volatile
和 restrict
限定符(在限定符语义允许的情况下)的一个、两个或全部三个的组合。
[编辑] 类型组
- 对象类型:所有不是函数类型的类型
- 字符类型:char、signed char、unsigned char
- 整数类型:char、有符号整数类型、无符号整数类型、枚举类型
- 实数类型:整数类型和实浮点类型
- 算术类型:整数类型和浮点类型
- 标量类型:算术类型、指针类型和 nullptr_t(since C23)
- 聚合类型:数组类型和结构体类型
- 派生声明符类型:数组类型、函数类型和指针类型
构造一个完整的对象类型,使其对象表示形式中的字节数无法用 size_t 类型表示(即 sizeof
运算符的结果类型),包括在运行时形成这样的 VLA 类型,(since C99) 是未定义行为。
[编辑] 兼容类型
在 C 程序中,不同翻译单元中引用同一对象或函数的声明不必使用相同的类型。它们只需要使用足够相似的类型,正式称为兼容类型。这同样适用于函数调用和左值访问;参数类型必须与参数类型兼容,左值表达式类型必须与被访问的对象类型兼容。
如果类型 T
和 U
兼容,则:
- 它们是相同的类型(相同的名称或由
typedef
引入的别名) - 它们是兼容的非限定类型的完全相同的 cvr 限定版本
- 它们是指针类型,并且指向兼容的类型
- 它们是数组类型,并且
- 它们的元素类型是兼容的,并且
- 如果两者都具有恒定大小,则该大小相同。注意:未知边界的数组与任何具有兼容元素类型的数组兼容。VLA 与任何具有兼容元素类型的数组兼容。(since C99)
- 它们都是结构体/联合体/枚举类型,并且
- (C99)如果其中一个是用标签声明的,则另一个也必须用相同的标签声明。
- 如果两者都是完整类型,则它们的成员必须在数量上完全对应,用兼容的类型声明,并具有匹配的名称。
- 此外,如果它们是枚举,则对应的成员也必须具有相同的值。
- 此外,如果它们是结构体或联合体,
- 对应的成员必须以相同的顺序声明(仅限结构体)
- 对应的 位域 必须具有相同的宽度。
- 一个是枚举类型,另一个是该枚举的底层类型
- 它们是函数类型,并且
- 它们的返回类型是兼容的
- 它们都使用参数列表,参数的数量(包括省略号的使用)相同,并且在应用数组到指针和函数到指针类型调整并剥离顶层限定符后,对应的参数具有兼容的类型
|
(until 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++ 程序。
[编辑] 复合类型
复合类型可以从两个兼容的类型构造;它是一种与这两种类型都兼容的类型,并且满足以下条件
- 如果两种类型都是数组类型,则应用以下规则
- 如果一种类型是已知恒定大小的数组,则复合类型是该大小的数组。
|
(since C99) |
- 否则,两种类型都是未知大小的数组,复合类型是未知大小的数组。
- 复合类型的元素类型是两种元素类型的复合类型。
|
(until 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"
类型名称在以下情况下使用
(since C99) | |
(since 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++ 文档 关于 Type
|