C_深入了解

有句话说得好,C是万物之源
C作为编程入门的首选语言已流行数十年,作为使用C的编程者,理应深入了解其基本特性
此篇文章记录我在学习C语言过程中遇到的困难,整理成知识点展列如下:

//随着学习的深入不定时更新,最后一次更新时间:2022_01_13

缓冲区与scanf函数及其错误格式输入

用户的输入分为缓冲输入与无缓冲输入,无缓冲输入为回显用户输入的字符后立即重复打印该字符,缓冲输入为用户输入的字符被收集并储存在一个被称为缓冲区(buffer)的临时存储区,按下Enter键后,程序才可使用用户输入的字符
在这里插入图片描述
  为什么要有缓冲区?首先,把若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果用户打错字符,可以直接通过键盘修正错误,当最后按下Enter键时,传输的是正确的输入。 虽然缓冲输入好处很多,但是某些交互式程序也需要无缓冲输入。例如,在游戏中,用户希望按下一个键就执行相应的指令。因此,缓冲输入和无缓冲输入都有用武之地。
  缓冲分为两类:完全缓冲I/O和行缓冲I/O。完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是 512 字节和4096字节。行缓冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,在按下Enter键后才刷新缓冲区。
  直接调用操作系统的函数被称为底层 I/O,由于计算机系统存在差异,C不可能为普通的底层I/O函数创建标准库,然而从较高层面上,C还可以通过标准I/O包来处理文件。引入标准I/O的概念我们首先介绍计算机系统的差异,如不同的系统储存文件的方式不同。 有些系统把文件的内容储存在一处,而文件相关的信息储存在另一处;有些系统在文件中创建一份文件描述。在处理文件方面,有些系统使用单个换行符标记行末尾,而其他系统可能使用回车符和换行符的组合来表示行末尾。 有些系统用最小字节来衡量文件的大小,有些系统则以字节块的大小来衡量。如果使用标准 I/O 包,就不用考虑这些差异。因此,可以用 if (ch == ‘\n’)检查换行符。即使系统实际用的是回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
  C程序处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的输入,由属性更统一的流来表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
  C语言中的scanf函数为标准I/O包的成员,可以实现用户的输入,对标准输入流stdin进行处理,键盘输入采取行缓冲模式,我们来了解一下scanf函数

int scanf(const char *format, ...)

用户的输入数据存入缓冲区,输入’\n’指令后,scanf函数对其进行扫描,依次按format形式赋给变量,如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,返回EOF.

在编程人员设计程序时,往往希望实现这样的功能:如果用户输入错误,提示用户输入正确格式,并重新输入,如下:

//要求用户输入数字,否则报错,重新输入
while (!scanf("%d", &x)) {
	printf("Format Error.Please try again.\n");
}

那么scanf的问题随之而来,如果用户输入错误,错误格式的输入会滞留在缓冲区内,下一次scanf从缓冲区内读取时,会直接读取他而不是用户的输入从而报错,并陷入无限循环,显然这不是用户想要看到的
怎么处理呢?这时候就用到了getchar()函数,getchar函数可以读取缓冲区内的第一个字符,因此可设计读到’\n’才退出的循环以清空缓冲区,故程序可进行如下改良

while (1) {
	if (!scanf("%d", &x)) {
	while (getchar() != '\n');
		printf("Format Error.Please try again.\n");
		continue;
	} 
	break;
}

显然,问题得到了改善,但没有完全解决,假如用户在一组输入里输入了正确的格式混合错误的格式,如要求int型输入下输入了111aaa,scanf会先扫描到’1’,直到’a’出现,把111存入目标变量中,aaa留在缓冲区中,并返回1,不会进入if条件判定,如此以来情况更恶劣了,不但没检测到用户的错误输入,还把错误字符存入了缓冲区,影响之后检测输入的正确性
那么如何处理呢?这时候需要用到getchar()函数与goto语句,由于用户输入正确的格式后缓冲区内会留下’\n’,在判定用户输入正确后可进行二次判定,如果getchar()读取缓冲区内不是’\n’,代表用户输入正确的部分之后有错误冗余并清除缓冲区,之后用goto语句回到第一次输入,改良如下

while (1) {
	retry:
		if (!scanf("%d", &x)) {
			while (getchar() != '\n');
			printf("Format Error.Please try again.\n");
			continue;
		} 
		break;
	}
	if (getchar()!= '\n') {
		while (getchar() != '\n');
		printf("Format Error.Please try again.\n");
		goto retry;
	}

类型在内存中的存储与printf函数的输出规则

类型的存储规则如下:

  • char: 以ASCII码值的二进制形式存储,占用1字节,8bit位
  • short、int、long:以补码形式存储,无符号类型第1bit位为符号位,1为负,0为正
  • float:占用4字节,32bit位,第1位为符号位S,接着8bit位为指数E,剩下23位为有效数字M

在这里插入图片描述
符号位S: 1为负,0为正
指数位E与有效数字M:
将浮点数绝对值的二进制形式表出,假设为10110101,将其转变为1.0110101 * 27 ,将指数7加上32位的阶码数127以无符号整型存储在E中,将小数点后的0110101按序存储在M中,不足的用0补满,如有超过在尾部截断
如果E内全为1,值为无穷
如果E内全为0,值为0

  • double:占用8字节,64bit位,第1位为符号位S,接着11bit位为指数E,剩下52位为有效数字M
    在这里插入图片描述
    存储模式与浮点型类似,64位的阶码数为1023

在C标准库中,printf()函数是标准输出函数,按照用户输入的格式读取变量,如果变量与用户输入的格式不相等,会不可预知的产生错误
如:按%d格式化打印float num = 9.0 ,32位系统的结果如下
在这里插入图片描述
浮点数9.0等于二进制1001.0,即1.001 * 23
因此在32位系统下 S = 0, E=3+127=130 , M=1.001, 二进制存储形式为
0 10000010 00100000000000000000000
其二进制表示的值为:1091567616,与结果相符

强制类型转换

在语句和表达式中如果使用混合类型,C会采用以下规则进行自动类型转换:

  1. 算术运算式中,低类型转换为高类型
  2. 函数传参时,实参类型转换为形参类型
  3. 赋值表达式中,表达式右侧类型转换为表达式左侧类型
  4. 存在返回值的函数中,返回表达式类型转换为返回值类型
  5. float在参与传参时,转换为double (K&R时代的C,float参与运算也会转换为double以提高精度)
  6. char、short在参与运算、传参时,发生整形提升,转换为int,整形提升规则为:在内存中存储的补码,全补符号位至4字节长

类型级别的高低如下所示:
在这里插入图片描述
除了C语言内的隐士转换,编程者还可以根据强制转换语句进行类型转换,转换规则如下:
向高类型转换:

  1. short、char转换为int,发生整形提升
  2. int、long转换为float、double,数值不变,补齐小数点位,按float、double存储规则进行存储

向低类型转换:

  1. float、double转换为int、long,数值不变,去除小数点位,按int、long
  2. 其余情况均发生截断

针对上述规则举几个例子:

char ch;
int i;
float fl;

变量赋值:

fl = i = ch = 'C';

字符’C’被作为1字节的ASCII值储存在ch中.整数变量i 接受由’C’转换的整数,即按4字节储存67.最后,fl接受由67转换的浮点数 67.00

整形提升与截断:

ch = 'C';
ch = ch + 1;

字符变量’C’参与运算,发生整形提升转换为int,值为67,然后运算,结果+1变为68,截断成1字节储存在ch中

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值