37. 使用%f转换说明打印十进制计数法的float或者double,用%e打印指数计数法的浮点数。如果系统支持十六进制格式的浮点数,可用a和A分别代替e和E。打印long double类型使用%Lf、%Le或者%La。
38. 当计算导致数字超过当前浮点型能表示的最大值,就会发生上溢,这种行为在过去是未定义的,但现在C语言规定,这种情况下,会赋予一个表示无穷大的特定值。
39. 假设一个数字是float类型能用全部精度 表示的最小的数,现在将它除以2,这时候指数会减少,但如果指数已经是最小值的时候,计算机只能将尾数部分的位向右移,此时,虽然得到结果,但是却损失了原有末尾的有效数字,这种情况叫下溢,C语言将损失了类型全精度的浮点值称为低于正常的浮点值。目前,C库已经提供了用于检查计算是否会产生低于正常值的函数。
40. 另一个特殊的浮点值为NaN(not a number)。比如给asin()函数传递一个值,该函数返回一个角度值。但是正弦值不能超过1,如果传入大于1的数,该函数的行为是未定义,这时,函数返回一个NaN值。
41. C99标准支持复数类型和虚数类型,但是有所保留。一般而言,复数类型都是可选项。C11标准将整个复数软件包作为可选项。
42. C语言有3种复数类型:float_Complex、double_Complex、long double_Complex。每个复数变量包括两个基本类型的值,分别表示复数的实部和虚部。C语言的3种虚数类型:float_Imaginary、double_Imaginary和long double_Imaginary。如果包含complex.h头文件,便可用complex代替_Complex,用imaginary代替_Imaginary。还可用I代替-1的平方根。
43. Sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小。C99和C11提供%zd转换说明来匹配sizeof的返回类型。一些不支持C99和C11的编译器可用%u或%lu来代替%zd。
44. C语言定义了char类型是1个字节。而在char为16位、double类型为64位的系统中,sizeof给出的double是4字节。
45. C语言规定,当缓冲区满、遇到换行字符、或者需要输入的时候,此时会刷新缓冲区。另一种刷新缓冲区的方法是使用fflush()函数。
46. C语言允许混合数据类型的表达式,但是会进行自动类型转换。以便实际运算时统一使用一种类型。
47. 字符串是一个或者多个字符的序列。C语言没有专门用于存储字符串的变量类型,字符串都被存储在char类型的字符数组里面。数组由连续的存储单元组成。字符串以字符\0作为结束标记。
48. scanf函数在遇到空格时会结束输入。
49. C99和C11标准专门为sizeof运算符的返回类型添加了%zd转换说明,这个对于strlen()同样适用。对于早期的C,sizeof和strlen()返回的实际类型通常是unsigned或unsigned long。
50. Sizeof的运算对象是类型时,圆括号必不可少,但如果是特定量,则可有可无。
51. 用大写表示符号常量是C语言的一贯的传统。另一个不常用的命名约定是:在名称前加c_或k_前缀表示常量。符号常量的命名规则和变量相同,即可以使用大小写字母、数字和下划线,首字符不能为数字。
52. #define指令还可以定义字符和字符串常量。前者使用单引号,后者使用双引号。
53. C90标准新增了const关键字,用于限定一个变量为只读。
54. float.h和limits.h中定义了有关基本数据类型的一些明示常量,但是要注意的是,编译器要完全支持C99标准才能识别LLONG_MIN标识符。
55. 在最开始的时候,printf()和scanf()函数并非C语言中的I/O函数,C语言将输入输出的实现留给了编译器的作者,这样可以针对不同的机器更好的匹配输入/输出。但后来考虑到兼容性问题,各个编译器都提供不同版本的printf()和scanf(),这些版本之间偶尔有一些小差异,所以C90和C99标准规定了这些函数的标准版本。
56. ANSI C标准为printf()提供的转换说明如下:
转换说明 输出
%a 浮点数、十六进制数和P标记法(C99/C11)
%A 浮点数、十六进制数和P标记法(C99/C11)
%c 单个字符
%d 有符号十进制数
%e 浮点数,e计数法
%E 浮点数,e计数法
%f 浮点数,十进制计数法
%g 根据值的不同,自动选择%f或%e。%e格式用于制数小于-4或大于等于精度。
%G 根据值的不同,自动选择%f或%E。%E格式用于制数小于-4或大于等于精度。
%i 有符号十进制数(与%d相同)
%o 无符号八进制数
%p 指针
%s 字符串
%u 无符号十进制数
%x 无符号十六进制数,小写
%X 无符号十六进制数,大写
%% 打印一个%号
57. 需要注意的是,转换说明与后面打印的数据必须匹配,如果缺少打印的数据,有的系统也不会报错,但是会打印一些随机数字。
58. 在%与转换字符之间可以插入修饰符。下列为合法的修饰符。部分为C99新增,如果编译器不支持C99,则可能不支持部分修饰符。
59. sizeof运算符以字节为单位返回类型或值的大小。C语言只规定它的返回值是无符号整数,在不同的实现中,它可以是unsigned int、unsigned long甚至是unsigned long long。C语言提供了可移植性更好的类型,首先,stddef.h头文件中把size_t定义成系统使用sizeof返回的类型,这被称为底层类型。其次,printf()使用z修饰符表示打印相应的类型。同样,C还定义了ptrdiff_t类型和t修饰符来表示系统使用的两个地址差值的底层有符号整数类型。
60. 在K&R C中,表达式或参数中的float类型值会被自动转换为double类型,但一般而言,ANSI C不会把float自动转换为double。为了保护现有程序,printf()函数中所有float类型的参数(对未使用显式原型的所有C函数都有效),仍然自动转换为double类型。
61.
62. printf()函数具有返回值,它返回打印字符的个数。如果输入有错,则返回-1。这里的字符指的所有字符,包括空格和不可见的换行符等。
63. 给字符串断行有3中方式,第一种,加\n换行符,第二种加\。第三种,两个用双引号括起来的字符串之间用空白隔开,C编译器会将多个字符串看作一个字符串(ANSI C)。
64. 用scanf()函数读取基本变量类型的值,在变量前加一个&。而把字符串读入字符数组时,则不要使用&。
65. scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。唯一的例外是%c转换说明,根据%c,scanf()会读取每一个字符,包括空白。对于float类型和double类型,printf()都使用%f,%e,%E,%g和%G转换说明,而scanf()函数只把它们用于float类型,double使用l修饰符。
66.
67.
68. scanf()函数每次读一个字符,跳过所有的空白,从第一个非空白字符处开始读取,直到再次遇到空白字符(读数字时,以非数字作为结束标志)。如果使用了字段宽度,scanf()会在字段结尾或第一个空白符处停止读取。读取数字时,如果第一个非空白字符是A,而不是数字,则把A放回输入缓冲区中。而下一次读时,首先读到这个字符。
69. 当scanf()函数把字符串读入字符数组时,会在字符串序列的末尾加上\0。
70. scanf()函数允许把普通字符放在格式字符串中。输入时,必须遵守这个格式。但因为scanf()读入的时候,自动会跳过空白,所以输入时是可以换行的。除了%c,其他转换说明都会自动跳过待输入值前面的所有空白。
71. scanf()函数会返回成功读取的项数作为返回值。非法输入时,返回0,遇到“文件结尾”,返回EOF(stdio.h中定义的特殊值,通常用#define定义为-1)。
72. 如果不想预先指定字段宽度,希望通过程序来指定,可以用*修饰符来代替字段宽度。此时,需要一个额外的参数来告诉printf()函数,字段宽度是多少。而对于scanf()函数,把*放在%和转换字符之间,会使得scanf()函数跳过相应的输出项。
73. 在一些地方,用逗号来分隔数字的高位和低位,C语言针对这种情况,提出了本地化概念。C标准有两个本地化设置:“C”和“”(空字符串)。默认情况下,程序使用“C”本地化设置。
74. C90标准添加了一元+运算符。
75. 整数除法和浮点数除法不同。浮点数除法的结果是浮点数,整数除法的结果是整数。整数除法时,小数部分会被丢弃,这一过程称为截断。浮点数与整数做除法运算时,整数会被转换为浮点数。
76. C99标准之前,针对整数除法,一种方法是:舍入过程采用小于或等于浮点数的最大整数。另一种舍入方法为丢掉小数部分。这种称为“趋零截断”。C99以后,采用趋零截断。
77. 对负数进行求模运算,C99之前,处理的方法很多,但在此之后,负数求模之后为负数,整数求模之后为正数。
78. 递增运算符可以简化代码,并且它生成的机器代码,执行效率更高(但随着编译器的智能化,这一优势可能正在减弱),但是可读性差。
79. 单独使用递增运算符时,前递增和后递增结果一样,但是当和其它运算对象结合时,结果不一样。
80. 递增递减运算符的优先级很高,仅次于最高优先级:圆括号。递增递减运算符只能影响一个可修改的左值,对常量无效。
81. C语言中,编译器可以自行选择先对函数的哪一个参数求值,这样可以提供编译器的效率。但如果在函数的参数中,是用了递增递减函数,就可能会出现一些问题。对于一些表达式中,同样可能会出现这种问题。
82. 如果一个变量出现在函数的多个参数中,不要对该变量使用递增递减运算符。如果一个变量多次出现在一个表达式中,不要对该变量执行递增递减运算。
83. C把末尾加了分号的表达式看作是一条语句。需要注意的是,声明不是表达式语句。
84.
85. 完整表达式,就是指这个表达式不是另一个更大表达式的子表达式。
86.
87. 简单语句以分号结尾,复合语句由花括号括起来的一条或多条语句组成。
88. 当类型转换出现在表达式中,无论是unsigned还是signed的char和short都会被自动转为int。如有必要,会被转为unsigned int(如果short和int的大小相同,此时unsigned int就比int大)。在K&R那时,float会被转为double(目前的C不是这样),由于都是从较小类型转换到较大类型,因此称为“升级”。
89. 涉及两种类型的运算,两个值会被分别转换为两种类型中的更高级别。
90.
91.
92. 赋值语句中,计算的结果会被转换为被赋值的变量的类型。这个过程会导致升级或降级。
93. 作为函数参数传递时,char和short被转为int,float被转为double,但是函数原型会覆盖自动升级。
94. 如果目标类型是无符号整型,且待赋的值是整数,额外的位会被忽略。
95. 如果目标类型是有符号整数,且待赋的值是整数,结果因实现而异。
96. 如果目标类型是整型,且待赋的值是浮点数,该行为是未定义的(可能被截断)。
97. 如果函数不接受参数,函数头的括号里应该写void。
98. 在ANSI C之前,C使用的是函数声明,而不是函数原型。函数声明只指明了函数名和返回类型,并没有指明参数类型。