类型
(另见算术类型以获取大多数内置类型的详细信息,以及 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
运算符的结果类型)表示,包括在运行时形成这种可变长数组类型,(C99 起)是未定义行为。
[编辑] 兼容类型
在 C 程序中,在不同翻译单元中引用同一对象或函数的声明不必使用相同的类型。它们只需要使用足够相似的类型,形式上称为兼容类型。这同样适用于函数调用和左值访问;参数类型必须与参数类型兼容,左值表达式类型必须与访问的对象类型兼容。
类型 T
和 U
兼容,如果
- 它们是相同的类型(相同的名称或由
typedef
引入的别名) - 它们是兼容的非限定类型的相同 cvr-限定版本
- 它们是指针类型,并且指向兼容的类型
- 它们都是数组类型,并且
- 它们的元素类型兼容,并且
- 如果两者都具有常量大小,则该大小相同。注意:未知边界的数组与任何具有兼容元素类型的数组兼容。可变长数组与任何具有兼容元素类型的数组兼容。(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 Types (p: TBD)
- 6.2.6 Representations of types (p: TBD)
- 6.2.7 Compatible type and composite type (p: TBD)
- C17 标准 (ISO/IEC 9899:2018)
- 6.2.5 Types (p: 31-33)
- 6.2.6 Representations of types (p: 31-35)
- 6.2.7 Compatible type and composite type (p: 35-36)
- C11 标准 (ISO/IEC 9899:2011)
- 6.2.5 Types (p: 39-43)
- 6.2.6 Representations of types (p: 44-46)
- 6.2.7 Compatible type and composite type (p: 47-48)
- C99 标准 (ISO/IEC 9899:1999)
- 6.2.5 Types (p: 33-37)
- 6.2.6 Representations of types (p: 37-40)
- 6.2.7 Compatible type and composite type (p: 40-41)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.1.2.5 Types
- 3.1.2.6 Compatible type and composite type
[编辑] 另见
C++ 文档中的 类型
|