C语言 一个数等于自己的二进制字符后缀?

本章描述C语言的词法结构,即C源文件中可以出现的字符以及他们是如何形成词法单元(或称为标记)的。
C源文件就是从一个字符集中所选择的字符序列。C程序是使用下面这些字符编写的,这些字符是在ISO/IEC 10646的基本拉丁文(Basic Latin)部分定义的:
1. 52个大写和小写拉丁字母:
4. 水平制表符(HT)、垂直制表符(VT)和换页符(FF)等格式控制符。
5. 29个图形字符以及它们的官方名称(如表2-1所示)。
另外,还必须有办法把源程序划分为多个代码行。这个任务可以由一个字符或一个字符序列来完成,或者使用源字符集之外的其他机制(例如记录尾指示符)来完成。
有些使用本国字符集的国家并没有包含表2-1中的所有图形字符。C89(修正案1)定义了三字符组(trigraph)和标记重拼(token respelling),允许用ISO 646—1083不变性代码集(Invariant Code Set)编写C程序。
C源程序有时候还使用其他字符,包括:
1. 像退格符(BS)和回车符(CR)这样的格式字符。
2. 其他的基本拉丁字符,包括$符号(美元符号)、@(商用的AT)和`(重音)。
格式字符被看成是空格,除此之外不会影响源程序。其他图形字符只可能出现在注释、字符常量、字符串常量以及文件名中。

参考:基本拉丁文 第2.9节;字符常量 第2.7.3节;注释 第2.2节;字符编码 第2.1.3节;字符转义码 第2.7.6节;执行字符集 第2.2.1节;字符串常量 第2.7.4节;标记重拼 第2.4节;三字符组 第2.1.4节。

在执行C程序时,字符集的解释方式并不一定和编写C程序时所使用的字符集相同。执行字符集中的字符是用源字符集中的对应字符表示的,或者用以反斜杠(\)开头的特殊的字符转义序列表示。
除了前面所提到的标准字符之外,执行字符集还必须包括:
1. null字符,它必须被编码为0这个值。
2. 换行符,表示行末标志。
3. 警告、退格和回车字符。
null字符用于标志字符串的结束;换行符用于在输入/输出时把字符流分割为不同的行(如果换行符事实上就存在于执行环境的文本流中,对程序员无疑很有吸引力。但是,运行时函数库的实现可以采用自己的方式对它进行模拟。例如,换行符可以被转换为输出中的记录尾指示符,而记录尾指示符也可以被转换为输入中的换行符)。
和源字符集一样,在执行字符集中包含格式字符如退格符、水平制表符、垂直制表符、换页符、回车符也很常见。另外,它还可以使用特殊的转义序列表示源程序中的这些字符。
当C程序在同一台计算机上编译和执行时,源字符集和执行字符集是相同的。但有时候C程序是跨机器编译的,也就是在一台计算机(宿主计算机)上编译,但在另一台计算机(目标计算机)上执行。当编译器计算一个涉及字符的常量表达式的编译时值时,它必须使用目标计算机的编码方式,而不是采用最自然(对于编译器编写者而言)的源字符集编码方式。

参考:字符常量 第2.7.3节;注释 第2.2节;字符编码 第2.1.3节;字符集 第2.1节;执行字符集 第2.2.1节;常量表达式 第7.11节;转义字符 第2.7.5节;文本流 第15章。

在C源程序中,空格、行末符、垂直制表符、换页符和水平制表符(如果存在)合称为空白字符(稍后讨论的注释也属于空白)。这些字符将被忽略,除非它们用于分隔相邻的标记,或者出现在字符常量、字符串常量和#include文件名中。空白字符可以用于设置C程序的布局,使它更容易阅读。
行末字符(或字符序列)标志着一行源程序的结束。在有些编译器中,回车符、换页符和垂直制表符等格式字符都可以终止源代码行,因此它们合称为行分隔符。为了确定预处理器所处理的行,行终止符是非常重要的。紧随行分隔符之后的那个字符被认为是下一行的第1个字符。如果第1个字符就是行分隔符,这一行就结束(表示空行),接下来以此类推。
为了把一个源代码行延续到下一行,可以在第1行的末尾使用反斜杠字符(\),或者使用标准C的三字符组??/。这时,反斜杠和行末标志符将会被删除,并创建一个更长的逻辑源代码行。这个约定在预处理器命令以及字符串常量中总是有效的,这也是它最常用的场合,并且具有很好的移植性。标准C和许多非标准的编译器对它进行了扩展,使它可以作用于任何源程序行。从概念上说,源代码行的延续出现在预处理和C程序的词法分析之前,但在三字符组的处理以及任何多字节字符序列到源字符集的转换之后。
在标准C中,甚至是标记也可以跨越多行。下面这两行

如果一个编译器把所有的非标准源字符都看成是空格或行分隔符,它分别应该像处理空白字符和行末标志字符一样处理它们。标准C建议编译器在第一次读取源程序时把所有这类字符转换为某种规范的表示形式,以便进行上述的处理。但是,程序员应该意识到依赖这种做法可能造成的结果。例如,可以预料到出现在反斜杠后面的换页符将会被去除。
大多数C编译器对行延续符之前和之后的源代码行的最大长度施加了限制。C89要求编译器允许逻辑源代码行的长度至少为509个字符;C99则要求至少允许4095个字符。

参考:字符常量 第2.7.3节;预处理器词法转换 第3.2节;源字符集 第2.1.1节;字符串常量 第2.7.4节;标记 第2.3节;三字符组 第2.1.4节。

计算机的(执行)字符集中的每个字符都具有某种编码约定,也就是在计算机中具有一种数值表示形式。这种编码是非常重要的,因为C把字符转换为整数,这个整数的值就是该字符的编码约定。前面所列出的所有标准字符必须都具有唯一的、非负的整数编码形式。
一种常见的C编程错误就是以为计算机所使用的是某种编码方案,但实际上它所使用的是另一种编码方案。
C表达式'z'-'A'+ 1计算Z和A的编码值之差加上1的结果,也就是字母表中的字符数量。事实上,在ASCII字符集编码方案中,这个结果是26。但是,在EBCDIC编码方案中,字母并不是连续编码的,这个计算的结果是41。
参考:源字符集和执行字符集 第2.1.1节。
标准C增加了一组三字符组,使C程序可以只使用ISO 646-1083不变性字符集来编写。这个字符集是7位的ASCII码的一个子集,它在许多非英语国家的字符集中也极为常用。表2-2列出了由两个连续的问号字符所引入的三字符组。标准C还提供了一些标记的重拼(第2.4节)和

头文件,后者定义了一些可用于替换一些操作符的宏。但是,和三字符组不同,这些宏并不会被看成是字符串和字符常量。

在源程序中,三字符组的替换出现在词法分析(标记化)以及字符串和字符串常量中的转义字符被确认之前。C编译器一共认识9个三字符组,所有其他字符序列(例如??&)都不会被当成是三字符组而进行转换。另外,一个新的转义字符\?可以防止对类似三字符组的字符序列进行转换。
如果需要在一个字符串中包含一个三字符序列,但这三个字符在正常情况下将被解释为一个三字符组,必须使用反斜杠转义字符引用这三个字符中的至少一个字符。因此,字符串常量“What?\?!”实际上所表示的是字符串常量“What??!”。
如果需要在一个字符串中包含一个反斜杠字符,必须使用连续2个反斜杠(第1个引用第2个)。另外,每个反斜杠可以转换为对应的三字符组。因此,字符串“??/??/”表示一个包含了单个\字符的字符串。

参考:字符集 第2.1节;转义字符 第2.7.5节;iso646.h 第11.9节;字符串连接 第2.7.4节;标记重拼 第2.4节。
为了容纳可能包含大量字符的非英语字母,标准C引入了宽字符和宽字符串的概念。为了表示宽字符以及外部世界中面向字节的字符串,标准C还引入了多字节字符的概念。C89修正案1提供了扩展,以处理宽字符和多字节字符。
set)中的元素的二进制表示形式。它的类型是整数类型wchar_t,这个类型是在头文件stddef.h中声明的。C89修正案1添加了整数类型wint_t,它必须可以表示wchar_t类型的所有值,并且可以表示一个额外的、可以区分的非宽字符值,用WEOF表示。标准C并没有指定任何宽字符编码方案,但0这个值被保留为“空的宽字符值”。宽字符常量可以用一种特殊的常量语法来表示(第2.7.3节)。
如果编译器厂商选择不支持扩展字符集(美国的C编译器厂商经常采用这种做法),wchar_t可以被定义为char,“扩展字符集”就与常规的字符集相同。
宽字符串(wide string)是以一个null宽字符结尾的连续的宽字符序列。null宽字符就是表示形式为0的宽字符。除了null宽字符以及另外的WEOF之外,标准C并没有指定扩展字符集的编码方案。宽字符常量可以用特殊的字符串常量来指定(第2.7.4节)。
多字节字符 在C程序中,宽字符可以作为一个单独的单元进行操作,但大多数外部媒介(例如文件)和C源程序是基于单个字节长度的字符的。熟悉扩展字符集的程序员设计了多字节编码(multibyte encoding),这是一种区域特定的方法,在字节长度的字符序列和宽字符序列之间进行映射。
多字节字符是源字符集或执行字符集中宽字符的表示形式(它们可能具有不同的编码形式)。因此,多字节字符串是常规的C字符串,但是这些字符可以被解释为一系列的多字节字符。多字节字符的形式以及多字节字符和宽字符之间的映射取决于定义的实现方式。在编译时,这种映射是针对宽字符常量和宽字符串常量执行的,标准函数库提供了一些函数,可以在运行时执行这种映射。
多节字字符可以使用依赖状态的编码(state-depedent encoding),即多字节字符的解释可能取决于前一个出现的多字节字符。一般情况下,这种编码利用了转移字符。这是一种控制字符,属于多字节字符的一部分,用于更改当前以及后续字符的解释。在一个多字节字符序列中,当前解释被称为编码的转换状态(conversion state),或称转移状态(shift state)。开始对一个多字节字符序列进行转换时,总是使用一个可区分的初始转换(转移)状态,并且这个状态经常在转换结束时被返回。
A编码(我们在这个例子中所使用的一种假设编码)是一种取决于状态的编码方案,它具有两个转移状态:“上”和“下”。↑字符把转移状态修改为“上”,↓字符把转移状态修改为“下”。“下”状态是初始状态,所有的非转移字符具有常规的解释。在“上”状态中,每个多字节字符由一对字母数字字符组成,它们以一种我们并未指定的方式定义了一个宽字符。
在下面这些字符序列中,每个序列包含了3个A编码形式的多字节字符,都从初始的转换状态开始。
最后一个字符串包含了并非严格必需的转移字符。如果允许冗余的转移序列,多字节字符可以变成任意长度(例如↓↓…↓x)。除非知道转换状态位于多字节字符序列的开始,否则就无法对诸如abcdef这样的序列进行解析,它既可以表示3个也可以表示6位宽字符。
ab|?x这个序列在A编码形式下是非法的,因为在“上”转移状态中出现了非字母数字字符。a↑b这个序列是非法的,因为最后一个多字节字符过早结束。
多字节字符也可以使用状态无关编码(state-independent encoding),即一个多字节字符的解释并不依赖于前一个多字节字符(尽管可能需要在一个多字节序列中从头开始寻找一个在字符串中间开始的多字节字符)。例如,C的转义字符的语法(第2.7.5节)表示一种char类型的状态独立编码,因为反斜杠字符(\)改变了它后面的一个或多个字符的解释,形成一个char类型的值。
B编码是另一种假设的编码方案,它是一种状态无关编码,并使用了一个特殊的字符,用表示,用于更改它后面的非null字符的含义。在B编码方案中,下面这些字符序列各自包含了3个多字节字符:
在B编码方案中,▽▽▽这个序列是非法的,因为它的最后并不是一个非null字符。
标准C对多字节字符设置了一些限制:
1. 标准字符集中的所有字符都必须出现在编码中。
2. 在初始的转移状态中,标准字符集中的所有单字节字符都保留正常的解释,并不影响转换状态。
3. 包含全0的字符不管是什么转移状态都被认为是null字符。多字节字符不能使用全0的字节作为它的第二个或后续的字符。
总而言之,这些规则保证多字节序列能够作为正常的C字符串(即中间不会嵌入null字符)处理。并且,没有特殊多字节编码的C字符和多字节序列一样可以得到预期的解释。
多字节字符的源代码和执行用法 多字节字符可以出现在注释、标识符、预处理器头文件名、字符串常量和字符常量中。每个注释、标识符、预处理器头文件名、字符串常量和字符常量必须以初始转移状态开始并结束,并且必须由合法的多字节字符序列组成。源代码的物理表示形式中的多字节字符在任何词法分析、预处理或甚至延续行的连接之前就被确认。
一个日文文本编辑程序允许在字符串常量和注释中使用日文字符。如果文本被写入到一个字节流文件中,则日文字符将被转换为多字节序列,这对于字符串常量而言是可以接受的,可以被标准C编辑器所理解。
在预处理期间,字符串和字符常量中的字符被转换为执行字符集,然后才被解释为多字节序列。因此,在形成多字节字符时,转义序列能使用到。在这个阶段之前,程序中的注释已经被删除,因此多字节注释中的转义序列可能没有什么意义。
如果源字符集和执行字符集相同,并且如果'a'的值在执行字符集为1418,那么字符串常量"aa"所包含的两个多字节字符与"\141\141"相同(B编码方案)。
在标准C中,可以使用两种方法编写注释。传统的注释从两个字符/*开始,以接下来出现的第1对*/结束。注释可以包含任意数量的字符,并且总是被当成空白看待。
从C99开始,注释也可以从//字符开始,并延续到下一个行分隔符(但并不包括这个行分隔符)。这有可能会破坏旧式C程序的代码(不过可能性极小)。作为练习,读者可以判断在什么条件下会出现这种情况。
如果注释位于字符串或字符常量内部,或者位于其他注释的内部,它并不会被编译器当做注释。除了能够认识注释中的多字节字符和行分隔符之外,C编译器并不会检查注释的内容。
下面这个程序包含了4个合法的C注释:
在预处理之前,编译器会删除注释,因此注释内部的预处理器命令将无法被认识,注释内部的行分隔符也不会终止预处理器命令。
下面这两个#define命令具有相同的效果:

标准C规定,所有的注释被一个空格字符所代替,以便于对C程序作进一步的转换。但是,有些旧式的实现并不会插入任何空白字符。这将影响预处理器的行为,详情将在第3.3.9节讨论。
有一些非标准的C编译器实现了“可嵌套注释”,即注释中的每一对/*必须存在对应的*/。这并不是标准的实现,程序员不应该依赖这个特性。为了使程序保持兼容,注释的内部不应该包含/*字符。
为了使编译器忽略一个C程序的很大一部分代码,最好使用预处理器命令封装这块代码:
这个方法优于在文本前后插入/*和*/。如果采用后面这种方法,如果这段代码内部也存在注释,就不会担心出现/*-风格问题。
参考:#if预处理器命令 第3.5.1节;预处理器词法约定 第3.2节;空白 第2.1节。
根据本章剩余部分所讨论的规则,组成C程序的字符被收集为词法标记。标记一共分为5种类型:操作符、分隔符、标识符、关键字和常量。
当编译器按照从左向右的顺序收集字符时,它总是尽可能形成最长可能的标记,即使这样做所形成的并不是合法的C程序。相邻的标记可以用空白字符或注释进行分隔。为了防止混淆,标识符、关键字、整型常量、浮点型常量必须与后面的标识符、关键字、整型常量或浮点型常量分隔。
预处理器采用了略有不同的标记约定。具体地说,标准C预处理器把#和##看成是标记,这在传统C中是非法的。

在第4个例子中,b--x这个字符序列是非法的C语法。如果分解为b、-、-、x这些标记,虽然在语法上是合法的,但编译器并不允许进行这样的标记化。

参考:注释 第2.2节;常量 第2.7节;标识符 第2.5节;预处理器标记 第3.2节;关键字 第2.6节;标记合并 第3.3.9节;空白字符 第2.1节。
表2-3列出了C标记中的操作符和分隔符(标点符号)。为了帮助程序员使用没有美国英语字符的I/O设备,C提供了替代性拼写、<:>、%:和%:%,它们分别等于标点符号{、}、[、]、#、##。除了这些重拼之外,头文件iso64.h定义了对某些操作符进行扩展的宏。
在传统C中,复合赋值操作符被看成是两个独立的标记,也就是1个操作符和1个等号,可以用空格进行分隔。在标准C中,复合赋值操作符被看成是一个单独的标记。
参考:复合赋值操作符 第7.9.2节;iso64.h 第11.9节;预处理器标记 第3.2节;三字符组 第2.1.4节。
标识符(或名称)由大小写拉丁字母、数字和下划线字符序列组成。标识符不能以数字开头,并且必须与现有的关键字不同。
从C99开始,标识符也可以包含统一字符名称(第2.9节)和其他因编译器而异的多字节字符。在使用统一字符名称时,不能把数字放在标识符的开始处,并且必须使用“类似字母”的字符,不能使用标点符号。C99标准(ISO/IEC ,附录D)和ISO/IEC TR 提供了一个准确的统一字符名称列表。
当两个标识符具有相同的拼写时(包括所有字母的大小写也必须相同),它们就是相同的。也就是说,标识符abc和aBc是不同的。
除了避免使标识符的拼写与关键字相同之外,C程序员还必须避免自己所使用的标识符意外与标准函数库中的名称相同,不论是当前的标准函数库还是语言标准所描述的“未来函数库指南”部分。标准C保留了所有以下划线开头并且后面跟一个大写字母或另一个下划线的标识符。程序员应该避免使用这类标识符。C编译器有时会使用这些标识符,对标准C进行扩展(或者由于其他目的)。
C89要求编译器至少能够区分标识符的前31个字符,C99把这个要求增加到63个。在考虑这个要求时,每个统一字符名称或多字节字符都被看成是1个字符。
在标准C之前的编译器中,标识符的长度被限制在8个字符之内。因此,标识符countless和countlessone被认为是相同的标识符。一般而言,较长的名称可以提高程序的清晰性,减少发生错误的机会。使用下划线以及混合使用大小写字母可以提高较长标识符的可读性:
外部标识符(用存储类别extern声明的标识符)可能具有额外的拼写限制。这种标识符必须由其他软件(例如调试器或链接器)进行处理,在处理时可能会有更多的限制。C89所要求的最小容量是6个字符,并且不考虑大小写。C99把这个限制增加到31个字符,并区分大小写。但是,C99允许把统一字符名称当成是6个字符(最大为\U0000FFFF)或10个字符(\U或更高)。即使在C99之前,大多数编译器允许外部名字至少为31个字符。
当一个C编译器允许较长的外部标识符,但目标计算机却要求更短的外部名称时,可以使用预处理器隐藏这些较短的名称。在下面的代码中,一个外部错误处理函数具有较短(并因此比较难看明白)的名称eh73,但这个函数却用一个更具描述性的名称error_handler来表示。这是通过声明一个展开结果为eh73的预处理宏error_handler实现的。
有些编译器允许在标识符中使用前面所指定字符的之外的其他字符。标识符中常常允许使用美元符号($),使程序可以访问其他计算机系统所提供的特殊的非C库函数。
参考:#define命令 第3.3节;外部名称 第4.2.9节;关键字 第2.6节;多字节序列 第2.1.5节;保留的函数库标识符 第10.1.1节;统一字符名称 第2.9节。
表2-4所列出的标识符是标准C中的关键字,它们不能作为普通的标识符使用。它们可以作为宏名,因为所有的预处理都是在确认这些关键字之前进行的。关键字_Bool、_Complex、_Imaginary、inline和restrict是C99新增的。

下面的代码是少数适合使用与关键字相同的名称作为宏名的例子之一。这个定义允许非标准编译器生成那些使用了void关键字的程序。

C99引入了预定义标识符(predefined identifier)的概念,并定义了一个预定义标识符:__func__。它的性质和关键字相似,尽管它本身并不是关键字。和预定义宏不同,预定义标识符可以遵循正常的代码块作用域规则。和关键字一样,程序员无法自己定义预定义标识符。
如果下面这个声明出现在每个函数定义的左花括号之后,C99编译器就会隐式地声明标识符__func__:
这个标识符可以由调试工具使用,打印出外层函数的名称,如下所示:
把C程序移植到内存限制较为严格的目标计算机时,如果程序在运行时并不需要这些字符串,C编译器必须仔细地将它们去除。
参考:函数定义 第9.1节;预定义宏 第3.3.4节;作用域 第4.2.1节。
常量的词法类型包括4种不同类型的常量:整数、浮点数、字符和字符串:
这些标记在其他语言中称为字面值,以便与其他一些值为常量(即不会变化)但又不属于这些词法类型的对象进行区分。在C中,后面这类对象的一个例子是枚举常量,它属于标识符这个词法类型。在本节中,我们使用编译C的术语常量同时表示这两种情况。
每个常量都有值和类型。下面各节描述了不同类型的常量的格式。
  • 字母l或L表示long类型的常量
unsigned后缀可以按照任何顺序与long或long long后缀组合使用。小写字母l很容易与数字1混淆,因此应该避免作为后缀使用。
如果没有溢出,整数常量的值总是非负的。如果它的前面有个负号,它是应用于这个常量的单目操作符,它本身并不是常量的一部分。
整数常量的实际类型取决于它的长度、基数、后缀字母,类型表示形式取决于C编译器。确定整数常量类型的规则非常复杂,并且在非标准C、C89和C99中是不相同的。表2-5显示了所有的规则。
如果某种类型的整数常量的值超出了表2-5中它自己那组中最后一个类型的最大可表示整数,其结果是未定义的。在C99中,编译器可以把这些大常量赋值为一种扩展整数类型,然后再加上表中的符号约定(如果所有的标准选择都是有符号的,则扩展类型也必须是有符号的。如果所有的标准选择都是无符号的,则扩展类型也必须是无符号的。在其他情况下,有符号和无符号都是可以接受的)。在C89中,和整数类型的表示形式有关的信息是由头文件limits.h提供的。在C99中,头文件stdint.h和inttypes.h包含了额外的信息。
为了说明整数常量的一些微妙之处,假设int类型使用16位的补码表示形式,long类型使用32位的补码表示形式,long long类型使用64位的补码表示形式。我们在表2-6中列出了一些有趣的整数常量、它们的真实数学值、它们的类型(在常规情况下以及在标准C规则下),以及用于存储常量的实际C表示形式。
在这张表中,一个值得注意的有趣之处是215和216-1之间的整数在写成十进制常量时为正值,但在写成八进制或十六进制常量时(被转换为int类型)为负值。尽管存在这种异常的情况,但程序员很少受到整数常量值的困扰,因为常量的表示形式即使在出现问题时仍然是相同的。
C99通过INTN_C、UINTN_C和UINTMAX_C这些宏对整数常量的长度和类型实行可移植的控制。这些宏是在stdint.h中定义的。

a   所选择的类型是对应的组中能够不溢出地表示常量值的第1个类型。
b   如果所列出的类型都不够大,并且存在扩展类型,就可以使用这种扩展类型。

如果long类型采用32位的补码表示形式,下面这个程序可以判断实际使用的规则:

标准C的宽字符常量由前缀字母L所指定,它的类型是wchar_t,这是stddef.h头文件所定义的一种整数类型。宽字符常量的目的是允许C程序员表示那些无法用char类型表示的字符(例如日文字符)。宽字符常量一般由一系列的字符和转义码组成,一起形成了一个单独的多字节字符。多字节字符与对应的宽字符之间的映射是由编译器定义的,对应于mbtowc函数,这个函数在运行时执行多字节字符到宽字符的转换。如果多字节字符使用了转移状态编码,则宽字符常量必须以初始转移状态开始和结束。如果一个宽字符常量包含了超过1个的宽字符,它的值是由编译器所定义的。
多字符常量 整数常量和宽字符常量可以包含一系列的字符。把这个字符序列映射到执行字符集之后,仍然可能存在超过1个的执行字符。这种常量的含义是由编译器决定的。
旧式编译器所使用的一种约定是把4字节的整数常量表示了为一个4字符常量,例如'gR8t'。这种用法是不可移植的,因为有些计算机可能不允许这种做法,而且不同的计算机的整数类型的长度以及“字符顺序”(即字符包装为字的顺序)可能不同。
在一种4字节整数并且字节从左向右包装的ASCII实现中,'ABCD'的值将是('A'的值是0x42,'B'的值是0x42,接下来以此类推)。但是,如果使用了从右向左的包装,'ABCD'的值将是。

对于包含了n个字符的非宽字符串,在运行时将会静态分配一块可以容纳n+1个字符的内存,其中前n个字符是该字符串中的字符,最后一个字符是null字符'\0'。这块内存相当于这个字符串常量的值,它的类型是char [n+1]。类似,宽字符串常量由n个宽字符以及后面的一个null宽字符组成,它的类型是wchar_t [n +1]。
如果一个字符串常量不是&操作符的参数,也不是sizeof操作符的参数,也不是作为字符数组的初始化值,就可以应用寻常数组转换,把这个字符串转换为由一个指针所指向的字符数组(指针指向字符串中的第1个字符)。
char *p = "abcdef" 这个声明产生一个指针p,它被初始化为指向一块存储了7个字符的内存。这7个字符分别是'a'、'b'、'c'、'd'、'e'、'f'和'\0'。
单字符的字符串常量的值和字符常量的值是不同的。int X = (int) "A" ; 这个声明使X被初始化为一个指向存储了2个字符的内存块(包含'A'和'\0')的指针的整数值(前提是这个指针可以用int类型来表示)。但是,int Y = (int) 'A' ; 这个声明使Y被初始化'A'的字符码(在ISO 646编码中为0x41)。
字符串常量的存储 存储了字符串常量的字符的内存是无法修改的,因为它是只读的,也就是存在对修改的物理保护。有些函数(例如mktemp)要求向它传递指向字符串的指针,以便对字符串进行修改。因此,不要向这类函数传递字符串常量。反之,初始化一个包含了字符串常量的所有字符的字符数组(非const),并把这个数组的第1个元素的地址传递给这类函数。
p1、p2和p3的值都是指向字符数组的指针,但它们在写入能力方面各不相同。p1[0] = 'x'这个赋值总是可行。p2[0] = 'x'可能成功,也可能导致一个运行时错误。p3[0] = 'x'总是会导致一个编译错误,这是由于const的含义所致。
不要以为所有的字符串常量会存储在不同的地址。标准C允许编译器为两个包含相同字符的字符串常量使用相同的存储地址。
下面是一个简单的程序,显示了各种不同的字符串实现的区别。如果字符串常量是在只读内存中分配的,对string1[0]的赋值将会导致运行时错误。
字符串的延续 一个字符串常量一般出现在源程序文本的一行中。如果字符串太长,不方便写在一行中,可以在包含这个字符串的所有文本行(除最后一行外)后面加上一个反斜杠字符\。在这种情况下,反斜杠字符和行末符号将被忽略。这就允许把一个字符串常量分在多行书写。有些旧式的编译器会删除延续行的前导空白字符,但这是不正确的做法。
标准C会自动连接相邻的字符串常量和相邻的宽字符串常量,并在最后一个字符串的末尾添加一个null字符。因此,在标准C中,\延续机制的一种替代方法就是把长字符串分隔为几个字符串。在C99中,宽字符串和常规的字符串也可以按照这种方式进行连接,产生一个宽字符串常量。在C89中,这种做法是不允许的。
字符串s1的初始化在标准C和标准C之前的编译器中是允许的,但字符串s2的初始化则只被标准C所允许:
换行符(即执行字符集中的行末符)可以通过在字符中常量中增加一个转义序列\n插入到这个字符串中。这应该不会与字符串常量所使用的行延续符混淆。
在标准C中,字符串常量前面如果有一个前缀字母L,它就是个宽字符串常量,它的类型是“wchar_t的数组”。它表示来自一个扩展执行字符集的宽字符序列,可能为诸如日语这样的语言所使用。宽字符串常量中的字符是多字节字符串,将按照一种取决于编译器的方式映射到一个宽字符序列(mbstowcs函数在运行时执行类似的任务)。如果多字符字符使用了一种转换状态编码,这个宽字符串常量必须以起始转移状态开始和结束。
参考:数组类型 第5.4节;const类型指定符 第4.4.4节;数组类型的版本 第6.2.7节;转义字符 第2.7.5节;初始值 第4.6节;mbstowcs工具函数 第11.8节;mktemp工具函数 第15.16节;多字节字符 第2.1.5节;指针类型 第5.3节;预处理器词法约定 第3.2节;sizeof操作符 第7.5.2节;strlen工具函数 第13.4节;空白字符 参考:统一字符名称 第2.9节。
字符转义码用于表示一些常用的特殊字符,并且与目标计算机的字符集无关。表2-7列出了可以出现在反斜杠后面的字符以及它们的含义。

转义码\a一般映射到“响铃”或输出设备上的其他声音信号(例如ASCII的control-G,它的值是7)。转义码\?用于在较罕见的情况下表示问号(在这些情况下如果直接使用问号,可能会被误认为是三字符组的一部分)。
引用标记(")可能出现在没有前导反斜杠的字符常量中,撇号(')可能出现在没有反斜杠的字符串常量中。
为了说明如何使用字符转义码,下面是一个小程序,对输入的行数(实际上是换行符的数量)进行计数。getchar函数返回下一个输入字符,直到到达了输入的结束,此时getchar返回EOF宏的值,后者是在stdio.h中定义的。

数值转义码允许直接用八进制(在标准C中,还允许十六进制)编码值表示执行字符集中的一个字符。它最多允许使用3个八进制数字以及任意数量的十六进制数字。但是,标准C禁止使用unsigned char范围之外的值表示正常的字符常量,并禁止使用wchar_t范围之外的值表示宽字符常量。例如,在ASCII编码中,字符'a'可以被写成'141'或'x61',字符'?'可以写成'\77'或'x3F'。用于终止字符串的null字符总是写成\0。如果一个数值转义码的值并不对应于执行字符集中的某个字符,其结果由编译器决定。
下面这段简短的代码显示了数值转义码的用法。变量inchar的类型为int。
在使用数值转义码时,程序员必须小心,原因有二。首先,数值转义码可能依赖于字符编码,因此是不可移植的。用宏定义来隐藏转义码是一种更好的做法,这样可以方便地对它们进行修改:
其次,数值转义码的语法是脆弱的。当八进制数字达到3个或者遇到第1个字符不是八进制数字的字符时,八进制转义符就会终止。因此,字符串"\0111"由2个字符组成,分别是\011和1。字符串"\090"由3个字符组成,分别是\0、9和0。十六进制转义序列也会遇到这种终止问题,尤其是因为它们的长度可以是任意的。为了在一个字符串中停止一个标准C的十六进制转义码,可以分割字符串:
有些非标准的C编译器所提供的十六进制转义序列和八进制转义序列一样,只允许固定数量的十六进制数字。
参考:字符常量 第2.7.3节;#define 第3.3节;宏定义 第3.3节;null字符 第2.1节;字符串常量 第2.7.4节;执行字符集 第2.1节。 本节列出了C和C++的一些词法区别。
标准C的标记重拼和三字符组也是C++标准的一部分,但它们在标准C++之前的编译器中并不常见。C和C++都允许统一字符名称,它们所使用的语法相同,但只有C明确允许在标识符中使用其他由编译器所定义的字符(个人希望C++编译器以扩展的形式提供这个功能)。
C99的注释可以为C++所接受,反之亦然。在C99之前,标准C并不使用//作为注释起始符,因此字符序列//*在C和C++中的解释并不相同(它的细节作为练习留给读者)。
C++增加了3个新的复合操作符:
由于这些标记的组合在标准C程序中是非法的,所以不会影响C程序移植为C++程序。
表2-8所列出的标识符是C++的关键字,但不是C的关键字。但是,标准C保留了关键字wchar_t,C99还保留了关键字bool、true、false,作为标准函数库的一部分。

在C中,单字符常量的类型是int,但在C++中为char。多字符常量(它们依赖于编译器)在两种语言中都是int类型。在实际使用中,这点差别很少会产生问题,因为在整数上下文环境中所使用的C++字符常量会被隐式地提升为int类型。但是,sizeof('c')在C++中等于sizeof(char),但在C中等于sizeof(int)。
在C语言所产生的时代,人们尚未理解国际性的、多语言编程社区的需要。标准C对C语言进行了扩展,以容纳这个社区。本节简单介绍了标准C所解决的一些问题(以及相关的历史),使C语言对于非英语用户更为友好。
指令集和ASCII 每一种文化都根据一种可打印字母或符号的字符指令集进行书面交流。对于美国英语而言,这个指令集包括通常的52个大写和小写字母、十进制数字以及一些标点符号。这些字符一共大约100个,它们(由美国英语程序员和计算机生产厂商)通过一种7位的编码(称为ASCII)被赋予特定的二进制值。经过编码的字符出现在标准键盘上,并可以在C语言定义中找到。
遗憾的是,其他文化具有不同的指令集。例如,英国的英语使用者所使用的是6?皇?,但7位的ASCII码并没有包含这个字符。像俄语和希伯来语这样的语言使用和英语完全不同的字母,像汉语、日语和朝鲜语(CJK)这样的语言所使用的指令集由几千个符号组成。当今的程序员常常需要创建要求读者和写入多种语言的文本,包括他们的母语。他们还需要在程序中使用母语注释和母语变量名。他们所编写的程序需要移植到其他文化,至少在其他文件中不会成为非法(除非读者理解梵语并且读者的计算机可以显示梵语字符,否则读者就无法读懂梵语注释)。
这个问题逐渐被人们所认识,人们设计了一些不完整的解决方案,这些解决方案至今仍然得到支持。例如,ISO 646-1083不变性代码集被定义为ASCII的一个子集,在许多非英语的字符集中较为常用。人们又发明了一些方法,替换在更小的字符集中不存在的C字符,例如{、}、[、]和#。
Set,UCS)。它定义了一种4字节(或4个八位编码)的编码方案UCS-4,可以表示地球上所有文化的所有字符,并保留了充足的空间。UCS-4有一个非常有用的16位子集,称为基本多语言平面(Basic Multilingual Plane),即UCS-2,它由前两个字节为0的UCS-4编码所组成。UCS-2可以表示绝大多数文化的指令集,包括大约2万个CJK象形文字。但是,16位还不足以表示的有的字符,在计算机上操纵不低于32位的长度时显得不便,因此UCS-4是合适的方案。
Unicode字符集标准最初是由Unicode协会()所制订的一个16位的编码方案。Unicode 3.0现在完全与ISO/IEC 10646兼容。以前版本的Unicode只与UCS-2兼容。Unicode网站对字符编码提供了非常好的技术介绍。
字符表示形式大于传统的8位的字符称为宽字符。遗憾的是,8位(或7位)的字符并不容易根除。许多计算机和遗留的应用程序基于8位的字符,人们设计了计多方案,用8位或7位字符序列表示更大的字符指令集和宽字符。它们称为多字节编码或多字节字符。宽字符一般使用固定长度的表示形式,但多字节字符一般使用一个字节表示一些字节,使用两个字节表示另一些字符,使用三个字节表示其他一些字符,接下来以此类推。其中一个或多个8位字符被当成“转义”或“转移”字符,用于开始多字节序列。
我们今天在标准C中所看到的是一些技术的组合:有些方法用于处理明显的ASCII变型(三字符组和两字符组),有些方法用于处理完全现代的宽字符环境,有些方法用于处理I/O期间的多字节字符。最近,有一种方法可以按照一种可移植的方式表示任何文化适应的C程序(统一字符以及标识符中特定于本地文件的字符)。
统一字符名称   C99引入了一个概念,允许在字符常量、字符串常量和标识符中指定任何UCS-2或UCS-4字符。它的语法如下:
每个十六进制4位数包括4个十六进制数字,可以指定一个16位的值。十六进制4位数的值在ISO/IEC 10646中是以统一字符的4位数字和8位数字的“短标识符”指定的。\unnnn所指定的字符与\u0000nnnn所指定的字符相同。
C并不允许短标识符小于00A0的统一字符名称,但0024($)、0040(@)和0060(')例外。C也不允许短标识符在D800到DFFF范围内的统一字符名称。这些是控制字符,包括DELETE以及为UTF-16所保留的字符。使用标记合并创建统一字符名称的结果是未定义的。
参考:标识符和统一字符名称 第2.5节;标记合并 第3.3.9节。

}

在编程中,我们经常会用到浮点数。我们发现浮点数运算后的结果经常会有误差,也就是实际结果和理论结果不一致。 例如: 输出的理论结果应该是 0000。然而并不是,输出的实际结果为 0000 这就是浮点数的误差。 就像是钟表一样,都有一定的误差,在误差范围内,我们一般默认实际值为正确答案。 在已知误差和理论值的情况下,我们可以明确实际值的范围。

原因是因为计算机不能精确表示浮点数,计算机内部存在舍入误差在编程过程,不要直接比较两个浮点数,容易改变整个程序。 若想获得预期浮点数计算,可以使用

float类型数值个后缀F或者f ,没后缀F/f浮点数默认为double类型。 不能使用浮点数进行比较如果需要比较可以使用BigDecimal类 浮点数存在舍入误差,数字不能精确表示。如果需要进行不产生舍入误差精确数字计算,需要使用 BigDecimal类

0000,但是实际运行结果实际)却为0000这就说明程序在运行时产生了一定误差,而这个误差不可避免。这就像我们生活中用到时钟一样,时间长了就会有些不准确了。 于是我们写程序时就要重新定义个变量,来减少实际理论值之间误差C语言程序中表示理论值-误差<实际&&实际<理论值+误差程序我们误差精度。

计算机组成原理 【定点数 浮点数】不断更新 定点数 ==定点纯小数== ==定点纯整数== 浮点数 ==浮点数== ==IEE754== 定点表示:小数点位置固定 浮点表示:小数点位置不固定 定点数 纯小数 纯整数 带符号数 不带符号数 定点纯小数 定点纯整数 浮点数 浮点数 IEE754

}

ALT+b # 向后(左边)移动一个单词
ALT+d # 删除光标后(右边)一个单词
ALT+f # 向前(右边)移动一个单词

reset # 重置终端(当你不小心 cat 了一个二进制,终端状态乱掉时使用)


}

我要回帖

更多关于 二进制字符 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信