本章要点
- 关键字:
int
、short
、long
、unsigned
、char
、float
、double
、_Bool
、_Complex
、_Imaginary
- 运算符:sizeof()
- 函数:scanf()
- 整数类型和浮点数类型的区别
- 如何书写整型和浮点型常数,如何声明这些类型的变量
- 如何使用
printf()
和scanf()
函数读写不同类型的值
%.2f
%.2f
中的%.2
用于精准控制输出,指定输出的浮点数只显示小数点后面两位。
scanf()
scanf()
函数用于读取键盘的输入。%f
说明scanf()
要读取用户从键盘输入的浮点数,&weight
告诉scanf()
把输入的值赋给名为weight的变量。scanf()
函数使用&
符号表明找到weight变量的地点。scanf()
函数读取用户从键盘输入的数据,并把数据传递给程序;printf()
函数读取程序中的数据,并把数据显示在屏幕上。把两个函数结合起来,就可以建立人机双向通信。
浮点数
- 在一个值后面加上一个小数点,该值就成为一个浮点数。比如7是整数,7.00是浮点数。
- e计数法:3.16E7表示3.16*10^7。
- 浮点数和整数的储存方案不同。计算机把浮点数分为小数部分和指数部分来表示,而且要分开储存这两部分。比如在十进制下,可以把7.0写成0.7E1,这里0.7是小数部分,1是指数部分。当然,在计算机内部使用二进制和2的幂进行储存,而不是10的幂。
- C语言不把含小数点和指数的数作为整数。因此22和-44都是整型常量,但是22.0和2.2E1则不是。
八进制和十六进制
计算机如何知道一个数是十进制、十六进制还是二进制?比如10000。在C语言中,用特定的前缀表示使用哪种进制。
0x
或0X
前缀表示十六进制。所以十进制数16表示成十六进制是0x10
或者0X10
。0
前缀表示八进制。例如,十进制数16表示成八进制是020
(2*8^1)。- 要清楚,使用不同的进制数是为了方便,不会影响数被储存的方式。也就是说,无论把数字写成16、020或者0x10,储存该数的方式都相同,因为计算机内部都以二进制进行编码。
显示八进制和十六进制
不同的进制要使用不同的转换说明。
- 以十进制显示数字,使用
%d
。 - 以八进制显示数字,使用
%o
。 - 以十六进制显示数字,使用
%x
。 - 要显示各进制数的前缀
0
、0x
和0X
,必须分别使用%#o
(小写o表示八进制,不是0)、%#x
、%#X
。 - 虽然C允许使用大写或小写的常量后缀(比如
L
),但是在转换说明中只能用小写。
char类型
标准ASCII码的范围是0~127,只需7位二进制数即可表示。通常,char类型被定义为8位的存储单元,因此容纳标准ASCII码绰绰有余。
C语言把1字节定义为char类型占用的位(bit)数,因此无论是16还是32位系统,都可以使用char类型。
- 字符常量用单引号。
- 字符串用双引号。
非打印字符
如果要使用ASCII码,为何要写成\032
而不是032
?首先,\032
能更清晰地表达程序员使用字符编码的意图。其次,类似\032
这样的转义序列可以嵌入C的字符串中,如
printf("Hello!\007\n");
中就嵌入了\007
。
&符号
scanf("%c",&ch);
- scanf()函数会读取用户输入的字符,
&
符号表示把输入的字符赋给变量ch
。 - printf()函数中的转换说明(就是%xx)决定了数据的显示方式,而不是数据的储存方式。
有符号还是无符号
- 有些C编译器把char实现为有符号类型,这意味着char可表示的范围是
-128~127
。
因为保留一位为符号位,char本身8bit。那么就是2^7=128,整数里面带了一个0。
- 有些C编译器把char实现为无符号类型,那么char可表示的范围是
0~255
。
也就是一共2^8=256个数。
- 根据C90标准,C语言允许在关键字char前面使用
signed
或unsigned
。这样,无论编译器默认char是什么类型,signed char
表示有符号类型,而unsigned char
表示无符号类型。这在char类型处理小整数时很有用。
_Bool类型
- C99标准添加了_Bool类型,用于表示布尔值。
- 1表示true,0表示false。
- 所以_Bool类型实际上也是一种整数类型。但原则上它仅占用1位存储空间,因为对0和1而言,1位的存储空间足够了。
- 程序通过布尔值可选择执行哪部分代码。
float、double和long double
- 浮点数的表示使用指数计数法。该计数系统常用于表示非常大或非常小的数。
数字 | 指数计数法 |
---|---|
1000000000 | 1.0e9 |
123000 | 1.23e5 |
322.56 | 3.2256e2 |
0.000056 | 5.6e-5 |
- 通常,系统储存一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分(也叫作尾数或有效数)及其符号。
- double(双精度)类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。
浮点型常量
在代码中,可以用多种形式书写浮点型常量。浮点型常量的基本形式是:
有符号的数字(包括小数点),后面紧跟e或E,最后是一个有符号数表示10的指数。
如:
- -1.56E+12
- 2.87e-3
- 注:正号可以省略
可以没有小数点(如,2E5
)或指数部分(如,19.28
),但是不能同时省略两者。
可以省略小数部分(如,3.E16
)或整数部分(如,.45E-6
),但是不能同时省略两者。
下面是更多的有效浮点型常量示例:
- 3.14159
- .2
- 4e16
- .8E-5
-
用十六进制表示浮点型常量
C99标准添加了一种新的浮点型常量格式——用十六进制表示浮点型,即在十六进制数前加上十六进制前缀(0x或0X),用p和P分别代表e和E,用2的幂代替10的幂(即,p计数法)。如下所示:
0xa.1fp10
十六进制a等于十进制10,.1f
是1/16加上15/256,p10是2^10或1024。0xa.1fp10表示的值是(10+1/16+15/256)*1024,即十进制数10364.0。
注意,并非所有的编译器都支持C99的这一特性。
浮点数的上溢和下溢
类型大小
如何知道当前系统的指定类型的大小是多少?
sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小。C99和C11提供%zd
转换说明匹配sizeof的的返回类型。一些不支持C99和C11的编译器可用%u
或%lu
代替%zd
。
使用数据类型
把一个类型的数值初始化给不同类型的变量时,编译器会把值转换成与变量匹配的类型,这将导致部分数据丢失。例如,下面的初始化:
int cost = 12.99; //用double类型的值初始化int类型的变量
float pi = 3.1415926536; //用double类型的值初始化float类型的变量
- 第1个声明,cost的值是12。C编译器把浮点数转换成整数时,会直接截断小数部分,而不进行四舍五入。
- 第2个声明会损失一些精度,因为C只保证了float类型前6位的精度。
许多程序员和公司内部都有系统化的命名约定,在变量名中体现其类型。例如,用i_
前缀表示int类型,us_
前缀表示unsigned short类型。这样,一眼就能看出来i_smart是int类型的变量,us_versmart是unsigned short类型的变量。
参数和陷阱
float f = 7.0f;
float g = 8.0f;
printf("%d %d\n",f,g); /*值的类型不匹配*/
输出示例:
1606414344 1
或
0 1075576832
注意,用%d显示float类型的值,其值不会被转换成int类型。在不同的平台下,缺少参数或参数类型不匹配导致的结果不同。
转义序列示例
注:图中应该是int main(void)
而不是void main(void)
,笔误。
刷新输出
printf()何时把输出发送到屏幕上?最初,printf()语句把输出发送到一个叫作缓冲区(buffer)的中间存储区域,然后缓冲区中的内容再不断被发送到屏幕上。
C标准明确规定了何时把缓冲区中的内容发送到屏幕:
- 当缓冲区满、遇到换行字符或需要输入的时候(从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区)
例如,上图中前两个printf()语句既没有填满缓冲区,也没有换行符,但是下一条scanf()语句要求用户输入,这迫使printf()的输出被发送到屏幕上。
旧式编译器遇到scanf()也不会刷新缓冲区,程序会停在那里不显示任何提示内容,等待用户输入数据。在这种情况下,可以使用换行字符刷新缓冲区。代码应改为:
printf("Enter your desired monthly salary:\n");
scanf_s("%f",&salary);
无论接下来的输入是否能刷新缓冲区,代码都会正常运行。这将导致光标移至下一行起始处,用户无法在提示内容同一行输入数据。还有一种刷新缓冲区的方法是使用fflush()
函数。