声明(十七)
八、类型名
类型名(type names)具有以下语法格式:
specifier-qualifier-list abstract-declaratoropt
其中specifier-qualifier-list是说明符限定符列表,abstract-declarator是抽象声明符。抽象声明符具有以下语法格式:
(注:直接声明符(direct-declarator)必须包含标识符名;抽象声明符是描述类型,不需要绑定标识符。)
pointer
pointeropt direct-abstract-declarator
直接抽象声明符(direct-abstract-declarator)具有以下语法格式:
( abstract-declarator ) array-abstract-declarator attribute-specifier-sequenceopt function-abstract-declarator attribute-specifier-sequenceopt
数组抽象声明符(array-abstract-declarator)具有以下语法格式:
direct-abstract-declaratoropt [ type-qualifier-listopt assignment-expressionopt ] direct-abstract-declaratoropt [ static type-qualifier-listopt assignment-expression ] direct-abstract-declaratoropt [ type-qualifier-list static assignment-expression ] direct-abstract-declaratoropt [ * ]
函数抽象声明符(function-abstract-declarator)具有以下语法格式:
direct-abstract-declaratoropt ( parameter-type-listopt )
有些情况下需要指定一种类型,这可以通过类型名实现。语法上类型名是对该类型函数或者对象省略标识符的声明。
//int类型。 int //指向int类型对象的指针类型。 int * //含有5个数组元素的数组类型,每个数组元素都是指向int类型对象的指针类型。 int * [5] //未指定数组大小的变长数组类型,每个数组元素都是指向int类型对象的指针类型。 int * [*] //指向含有5个数组元素的数组的指针类型,每个数组元素都是int类型。 int (*)[5] //指向未指定数组大小的变长数组的指针类型,每个数组元素都是int类型。 int (*)[*] //无参数、返回类型为int *类型的函数类型。 int *() //指针类型,指向无参数、返回类型为int类型的函数类型。 int (*)() //未指定数组大小的数组类型, //数组元素是使用const类型限定符限定的指针, //指针指向含有一个int类型参数和一个double类型参数、返回类型是int类型的函数。 int (*const [])(int, double)
类型名中的空括号解释为无参数函数,而不是省略标识符的多余括号。
对于数组类型或者函数类型,直接抽象声明符中的可选属性说明符序列适用于前面的数组类型或者函数类型。属性说明符序列只影响其出现的声明中类型,而不影响涉及同一类型的其它声明。
//属性[[gnu::aligned(16)]]适用于数组类型。 int [3] [[gnu::aligned(16)]] //属性[[nodiscard]]适用于函数类型。 int [[nodiscard]] (void)
九、类型定义
在存储类说明符为typedef的声明中,每个声明符都定义一个标识符为typedef名,该typedef名表示该标识符指定的类型。typedef声明不会引入新类型,仅为指定类型提供别名。
typedef int Integer; Integer i; //等价于int i;。 typedef int *Ip; const Ip cp; //等价于int * const cp;。 typedef int Arr[5]; Arr arr; //等价于int arr[5];。 typedef int Func(void); Func func; //等价于int func(void);。
如果typedef名表示可变修改类型(variably modified type),应具有块作用域。
int n = 5; typedef int (*gvm)[n]; //如果gvm具有文件作用域,则非法。 ... { typedef int (*lvm)[n]; //lvm具有块作用域,所以合法。 }
如果typedef名表示变长数组类型,数组大小在typedef名定义时确定,而不是在使用时确定。程序执行时每次遇到包含typedef名的声明时,都会对与变长数组声明符和typeof运算符关联的数组大小表达式进行评估。
int n = 3; typedef int Arr[n]; Arr a1; //等价于int a1[3];。 n = 5; typedef int ARR[n]; ARR a2; //等价于int a2[5];。 Arr a3; //等价于int a3[3];。
typedef名与普通标识符具有相同命名空间,所以typedef名不能与普通标识符同名。
typedef int Integer; int Integer; //非法,名称冲突。
如果标识符可能解释为typedef名,也可能解释为非typedef名,类型说明符将使其解释为非typedef名。
typedef int Integer; struct s{ signed Integer : 2; //Integer是具有signed int类型的对象标识符。 const Integer : 2; //Integer是typedef名。 };
typedef名主要用途是改善代码的可读性。
typedef int F(double), (*Pf)(double); //以下三个函数声明是相同的。 int (*func(int (*)(double)))(double); F *func(F *); Pf func(Pf);
十、静态断言
静态断言声明(static assert declaration)具有以下两种语法格式:
static_assert ( constant-expression, string-literal ); static_assert ( constant-expression );
其中static_assert是C语言关键词,constant-expression是常量表达式,string-literal是字符串字面量。
(注:第二种语法格式是ISO/IEC 9899:2024标准新增内容。在ISO/IEC 9899:2024标准中static_assert是C语言关键词;但在之前的ISO标准中static_assert是<assert.h>头文件中定义的宏。)
静态断言用于在程序编译时测试断言。常量表达式(constant-expression)应为整数常量表达式。如果常量表达式的值不等于0,断言将不起作用;反之如果常量表达式的值等于0,将生成一条包含字符串字面量(string-literal)的诊断信息(如果静态断言包含字符串字面量。)。
//判断一个平台是否是64位平台。 static_assert( sizeof(void *) == 8, "This is not a 64-bit platform.\n" );
静态断言与动态断言的比较:
| 静态断言 | 动态断言 | |
| 断言时间 | 程序编译时。 | 程序执行时。 |
| 运行开销 | 无。 | 会增加运行开销,影响程序性能。 |
| 表达式 | 常量表达式。 | 标量表达式。 |
| 失败行为 | 编译时报错。 | 运行时终止或者抛出异常。 |