标识符
标识符(identifier)是由非数字字符和数字字符构成的序列,用以指定一个或者多个实体。本文将讨论C语言中标识符的通用规则、预定义标识符(predefined identifiers)、保留标识符(reserved identifiers)等三个方面。
一、标识符的通用规则
标识符可以包含以下5类字符:
- 数字(0~9);
- 下划线字符(_);
- 大写拉丁字母(A~Z),小写拉丁字母(a~z);
- 通用字符名;
- 其它实现定义的字符。
其中后4类字符又可统称为非数字字符。
C语言中标识符命名规则及其注意事项:
- 标识符不能以数字开头。
合法的标识符,例如:
Day1 you_next geng
非法的标识符,例如:
1day you-next dollar*
非法的原因:标识符1day以数字开头,而不是以非数字开头;标识符you-next和dollar*分别包含了未允许字符-和*。
- 标识符是区分大小写的。
标识符apple和Apple是两个不同的标识符。
- 标识符内不能存在空格。
例如:hello world这样的标识符是不允许的。
- 标识符不能与C语言关键词同名。
这是因为在编译的第7阶段当预处理标记转换成标记时,如果一个预处理标记既可以转换成关键词,又可以转换成标识符,将转换成关键词。
- 标识符不能与保留标识符同名。
如果同名,可能会导致编译错误或者链接错误,例如:自定义一个printf函数,编译时,编译器会给出类似error: conflicting types for 'printf'的出错信息。
- 标识符不宜太长。
ISO/IEC 9899:2018标准对标识符的最大长度未作明确限制,但具体实现中编译器能够识别的标识符的有效字符数(significant characters)是有限制的。根据ISO/IEC 9899:2018标准第5.2.4.1 Translation limits节,支持C标准的编译器对于内部标识符(内部标识符是宏名或者没有外部链接的标识符。)至少能够识别前63个字符;对于外部标识符(外部标识符是具有外部链接的标识符。)至少能够识别前31个字符。对于两个标识符,如果有效字符(significant character)存在差异,这两个标识符是不同标识符;如果只是非有效字符(nonsignificant characters)存在差异,其行为是未定义的。
- 标识符中通用字符名对应的字符在ISO/IEC 10646标准中编码值应为以下值之一:
00A8 00AA 00AD 00AF 00B2-00B5 00B7-00BA 00BC-00BE 00C0-00D6 00D8-00F6 00F8-00FF
0100-167F 1681-180D 180F-1FFF
200B-200D 202A-202E 203F-2040 2054 2060-206F
2070-218F 2460-24FF 2776-2793 2C00-2DFF 2E80-2FFF
3004-3007 3021-302F 3031-303F
3040-D7FF
F900-FD3D FD40-FDCF FDF0-FE44 FE47-FFFD
10000-1FFFD 20000-2FFFD 30000-3FFFD 40000-4FFFD 50000-5FFFD 60000-6FFFD 70000-7FFFD 80000-8FFFD 90000-9FFFD A0000-AFFFD B0000-BFFFD C0000-CFFFD D0000-DFFFD E0000-EFFFD
如果编码值为下述值之一,该通用字符名不可以用作标识符的首字符。
0300-036F 1DC0-1DFF 20D0-20FF FE20-FE2F
实现可能允许在标识符中使用不属于基本源字符集的多字节字符,这些字符及其对应的通用字符名由实现定义。在链接器不接受扩展字符的系统中,可以使用通用字符名来构成有效的外部标识符,例如:可以使用某些未使用的字符或者字符序列对通用字符名中的\u进行编码。扩展字符可以生成长外部标识符(long external identifier)。
二、预定义标识符
ISO/IEC 9899:2018标准提供了一个预定义标识符__func__,编译器应隐式地声明该标识符,其声明如下所示:
static const char __func__[] = "function-name";
其中function-name为函数名。
在函数中使用__func__标识符,可以获取函数名,例如:
void func(void) { puts(__func__); }
调用func函数,将输出:
func
预定义标识符是为实现保留的标识符,如果显式地声明与预定义标识符同名的标识符,其行为是未定义的。
三、保留标识符
保留标识符(reserved identifiers)包括标准库头文件中声明或者定义的标识符,未来标准库中可能声明或者定义的标识符,始终保留用于某种用途或者用作文件作用域标识符的标识符,具体有以下5种情况:
- 以两个下划线或者一个下划线和一个大写字母开始的标识符始终保留用于某种用途(与关键词相同的标识符除外。)。
- 在普通标识符和标记命名空间内,以下划线开头的标识符始终保留为文件作用域的标识符。
- 除非另有明确说明,否则在标准库头文件(包括未来标准库头文件)中定义的宏是保留的标识符。
- 在标准库头文件中声明的具有外部链接的标识符以及标识符errno始终保留为具有外部链接的标识符。(具有外部链接的保留标识符包括math_errhandling、setjmp、va_copy和va_end。)
- 在标准库头文件中声明或者定义的具有文件作用域的标识符保留用作宏名或者相同命名空间内具有文件作用域的标识符。
如果程序声明或者定义了一个保留标识符,或者将一个保留标识符定义成宏名,其行为是未定义的。
如果程序使用#undef指令删除第1种情况中的宏,其行为是未定义的。