Skip to content

Latest commit

 

History

History
217 lines (131 loc) · 9.2 KB

[snowming]-2021-8-15-【C基础】课时2:进制转换、命名规范、内存区域划分、向0取整、浮点数(二进制表示,在内存中的存储).md

File metadata and controls

217 lines (131 loc) · 9.2 KB

第3章:数据和C

本章要点

  • 关键字:intshortlongunsignedcharfloatdouble_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语言中,用特定的前缀表示使用哪种进制。

  • 0x0X前缀表示十六进制。所以十进制数16表示成十六进制是0x10或者0X10
  • 0前缀表示八进制。例如,十进制数16表示成八进制是020(2*8^1)。
  • 要清楚,使用不同的进制数是为了方便,不会影响数被储存的方式。也就是说,无论把数字写成16、020或者0x10,储存该数的方式都相同,因为计算机内部都以二进制进行编码。

显示八进制和十六进制

不同的进制要使用不同的转换说明。

  • 以十进制显示数字,使用%d
  • 以八进制显示数字,使用%o
  • 以十六进制显示数字,使用%x
  • 要显示各进制数的前缀00x0X,必须分别使用%#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前面使用signedunsigned。这样,无论编译器默认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的这一特性。

浮点数的上溢和下溢


类型大小

如何知道当前系统的指定类型的大小是多少?

Win10 64位 title

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类型。在不同的平台下,缺少参数或参数类型不匹配导致的结果不同。

转义序列示例

title

注:图中应该是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()函数。