词法元素(六)
3、枚举常量
枚举常量,即枚举成员,ISO/IEC 9899:2018标准规定:枚举常量类型应为int类型;枚举类型应与char类型、有符号整数类型或者无符号整数类型兼容。ISO/IEC 9899:2024标准规定:在无固定底层类型(fixed underlying type)的情况下,枚举常量类型可以是int类型,也可以是枚举类型;在未显式指定底层类型的情况下,枚举类型应与char类型、标准或者扩展有符号整数类型、标准或者扩展无符号整数类型中的一个兼容(bool类型和位精确整数类型除外。),具体由实现定义,但应能表示所有枚举常量值。
上述情况下,枚举常量的类型和枚举类型可能是相同类型,也可能是不同类型。
#define TYPE(x) _Generic((x), \ int: "int type", \ char: "char type", \ unsigned int: "unsigned int type", \ long: "long type", \ default: "other type" \ ) enum color {red, green, blue}; ... enum color myCar; printf("%s\n", TYPE(myCar)); //输出unsigned int type。 printf("%s\n", TYPE(red)); //输出int type。
这里枚举常量的类型是int类型;枚举类型是unsigned int类型。不同实现可能存在差异。
(注:使用gcc编译器编译,版本号12.4.0。)
ISO/IEC 9899:2024标准规定:枚举底层类型可以使用枚举类型说明符明确指定,指定的类型是枚举类型的固定底层类型。这种情况下枚举常量类型与枚举类型是相同类型,枚举类型与枚举固定底层类型兼容。
enum color : unsigned {red, green, blue}; enum color myCar;
枚举变量myCar与枚举常量red、green、blue具有相同类型,均与unsigned类型兼容。
默认情况下第一个枚举常量值为0,其后枚举常量值依次加1递增。
//枚举常量值分别为0,1,2,3,4。 enum weekday{ Sunday, Monday, Tuesday, Wednesday, Thursday };
可以使用=指定枚举常量值;其前面枚举常量具有默认值,其后面枚举常量将在指定值的基础上依次加1递增。
//枚举常量值分别为0,3,4,5,6。 enum weekday{ Sunday, Wednesday = 3, Thursday, Friday, Saturday };
表达式(或者常量表达式)中,可以使用整数的地方都可以使用枚举常量。
4、字符常量
C语言支持的字符常量可以使用以下格式表示:
encoding-prefixopt'c-char-sequence'
其中encoding-prefix是可选的编码前缀,例如:u8、L、u、U;c-char-sequence是由源字符集的任何成员(单引号、反斜杠、换行符除外)或者转义序列构成的序列。单引号、反斜杠、换行符可以使用转义序列表示,例如:\'、\\、\n。问号和双引号既可以使用自身表示,也可以使用转义序列表示,例如:?或者\?、"或者\"。
整数字符常量是使用单引号括起来的一个或者多个多字节字符的序列,例如:'a'。除u8前缀外,UTF-8字符常量与整数字符常量相同。wchar_t字符常量、UTF-16字符常量和UTF-32字符常量统称为宽字符常量(wide character constants),其前缀分别为L、u和U,例如:L'华'、u'\u534E'、U'\U0000534E'。
字符常量中的转义序列可以是简单转义序列(simple-escape-sequence)、八进制转义序列(octal-escape-sequence)、十六进制转义序列(hexadecimal-escape-sequence),也可以是通用字符名(universal-character-name),例如:\a、\123、\xAA、\u4E2D。
字符常量中允许的转义序列如下表所示。
| 转义序列 | 含义 | ASCII值 |
| \a | 警告符 -- 在不改变活动位置的情况下发出声音或者可视警告。 | 7 |
| \b | 退格符 -- 将活动位置移动至当前行的上一个位置。如果活动位置在行起始位置,其行为是不确定的。 | 8 |
| \t | 水平制表符 -- 将活动位置移动至当前行的下一个水平制表位置。如果活动位置处于或超过最后定义的水平制表位置,其行为是不确定的。 | 9 |
| \n | 换行符 -- 将活动位置移动至下一行的起始位置。 | 10 |
| \v | 垂直制表符 -- 将活动位置移动至下一个垂直制表位置的起始位置。如果活动位置处于或超过最后定义的垂直制表位置,其行为是不确定的。 | 11 |
| \f | 换页符 -- 将活动位置移动至下一个逻辑页开始的起始位置。 | 12 |
| \r | 回车符 -- 将活动位置移动至当前行的起始位置。 | 13 |
| \" | 双引号字符 | 34 |
| \' | 单引号字符 | 39 |
| \? | 问号字符 | 63 |
| \\ | 反斜杠字符 | 92 |
| \ooo | 1到3位八进制数表示的字符。 | |
| \xh[h…] | 十六进制数表示的字符。 | |
| \uhhhh \Uhhhhhhhh |
通用字符名表示的字符。 |
八进制转义序列中\后的八进制数视为是整数字符常量单个字符或者宽字符常量单个宽字符构成的一部分;这样形成的八进制整数值指定了对应字符或者宽字符的值,例如:'\110'对应的字符常量为'H',值为72。
十六进制转义序列中\x后的十六进制数视为是整数字符常量单个字符或者宽字符常量单个宽字符构成的一部分;这样形成的十六进制整数值指定了对应字符或者宽字符的值,例如:'\x48'对应的字符常量为'H',值为72。
八进制转义序列和十六进制转义序列都是由可以构成转义序列的最长字符序列组成。八进制转义序列在三个八进制数后结束,例如:'\1234'表示一个包含两个字符的整数字符常量,两个字符的值分别是'\123'和'4'。十六进制转义序列在遇到非十六进制字符结束,例如:'\x48M'表示一个包含两个字符的整数字符常量,两个字符的值分别是'\x48'和'M'。
八进制转义序列和十六进制转义序列的值应在对应类型可表示的值域范围内,具体如下表所示:
| 类别 | 前缀 | 对应类型 | 示例 |
| 整数字符常量 | 无 | unsigned char | '\x4C' |
| UTF-8字符常量 | u8 | char8_t | u8'\x4C' |
| 宽字符常量 | L | 与wchar_t类型对应的无符号类型 | L'\x4E2D' |
| u | char16_t | u'\x4E2D' | |
| U | char32_t | U'\x00004E2D' |
char8_t类型是用于8位字符的无符号整数类型,与unsigned char类型相同。char16_t类型是用于16位字符的无符号整数类型,与uint_least16_t类型相同。char32_t类型是用于32位字符的无符号整数类型,与uint_least32_t类型相同。wchar_t类型是一种整数类型,其值域范围能够表示当前语言环境扩展字符集的所有成员值。
UTF-8、UTF-16或者UTF-32字符常量只能包含一个字符,其值可以分别使用单个UTF-8、UTF-16或者UTF-32编码单元表示。
整数字符常量的类型是int类型。如果整数字符常量仅包含一个字符,该字符在文字编码中的映射值是该字符在文字编码的数值,并解释为一个整数,例如:整数字符常量'A'在ASCII字符集中,字符A的编码值是65,所以整数字符常量'A'的值为65。如果整数字符常量包含多个字符(例如:'ab'),或者包含不能在文字编码中映射为单一值的字符或者转义序列,其值将由实现定义,例如:整数字符常量'啊'在简体中文Windows系统中,其值是0XB0A1,这也是汉字啊在CP936中的编码。如果一个整数字符常量仅包含一个字符或者转义序列,其值是char类型的字符或者转义序列转换成int类型生成的结果值。
UTF-8字符常量的类型是char8_t类型。如果UTF-8字符常量不是通过十六进制转义序列或者八进制转义序列构成,那么UTF-8字符常量的值等于其在ISO/IEC 10646标准中的码点值(code point value),前提是码点值可以编码成一个UTF-8编码单元;否则UTF-8字符常量的值是十六进制转义序列或者八进制转义序列指定的值。
UTF-16字符常量的类型是char16_t类型。如果UTF-16字符常量不是通过十六进制转义序列或者八进制转义序列构成,那么UTF-16字符常量的值等于其在ISO/IEC 10646标准中的码点值(code point value),前提是码点值可以编码成一个UTF-16编码单元;否则UTF-16字符常量的值是十六进制转义序列或者八进制转义序列指定的值。
UTF-32字符常量的类型是char32_t类型。如果UTF-32字符常量不是通过十六进制转义序列或者八进制转义序列构成,那么UTF-32字符常量的值等于其在ISO/IEC 10646标准中的码点值(code point value),前提是码点值可以编码成一个UTF-32编码单元;否则UTF-32字符常量的值是十六进制转义序列或者八进制转义序列指定的值。
wchar_t字符常量的类型是wchar_t类型。如果wchar_t字符常量仅包含单个多字节字符,该字符映射到扩展执行字符集的单个成员,其值是实现定义的与多字节字符对应的宽字符,例如:宽字符常量L'啊'在简体中文Windows系统中,其值是0X554A。Windows系统中,宽字符使用UTF-16编码,0X554A也是汉字啊的UTF-16编码。如果wchar_t字符常量包含多个多字节字符,或者可以映射到扩展执行字符集多个成员的单个多字节字符,或者包含扩展执行字符集中未表示的多字节字符或者转义序列,其值将由实现定义。
'\0'通常用于表示空字符。如果实现未定义宏__STDC_MB_MIGHT_NEQ_WC__,基本字符集成员值与其在整数字符常量中作为单个字符的值相同。
对于使用二进制补码表示整数、使用8个位表示char类型对象的实现,如果char类型的值域范围与signed char类型相同,整数字符常量'\xFF'的值为-1;如果char类型的值域范围与unsigned char类型相同,整数字符常量'\xFF'的值为255。
5、预定义常量
预定义常量(predefined constants)是ISO/IEC 9899:2024标准新增内容,ISO/IEC 9899:2024标准提供了三个预定义常量:false、true、nullptr。
关键词false和true是bool类型常量,值分别是0和1。
(注:在ISO/IEC 9899:2018标准中,false和true是<stdbool.h>头文件中定义的宏。)
关键词nullptr表示空指针常量。
int *ptr = nullptr;