学嵌入式C语言,看这一篇就够了(4)
C语言的输入输出
C语言标准在发布的同时,ANSI组织同时也一起发布了和C语言相关的函数库,也就是标准C库,标准C库集成了很多的API函数接口,比如常用的输入和输出函数就是标准C库提供的
用户如果打算使用标准C库的函数,就必须要包含函数库对应的头文件,比如输入输出函数对应的头文件就叫做stdio.h
- stdio指的是Standard Input Output(标准输入输出)
- 在linux系统下stdio.h的位置在 /usr/include/stdio.h
格式化输出
一般标准C库中提供了很多关于输出的函数接口,其中最常用的就是printf()函数
(1)标志说明
(2)字符宽度
(3)转换精度
(4)长度修饰
注意:计算机内部存储多字节的数据时会涉及到大端小端,不同的处理器架构采用的模式是不同的,一般X86架构采用小端模式,ARM架构一般采用大端模式(但是并不绝对)!!!!!!
一般用union可以简单判断系统大小端
/*****************************************************************
*
* file name : endian.c
* author : 18408107475@163.com
* date : 2023/02/06
* function : 判断系统是大小端
* note : None
*
* CopyRight (c) 2023 18408107475@163.com All Right Reseverd
******************************************************************/
#include <stdio.h>
/*
联合体共用一块地址,对 int a 赋值时也能对 char b 赋值
*/
union Endian
{
int a; //4个byte → 32bits → 0x00000000 → 0x12345678
char b; //1byte → 8bits → 0x00 → 0x??
}u;
int main()
{
u.a = 0x12345678;
if(u.b == 0x78){
printf("当前系统是小端序(Little - endian)\n");
printf("%#x\n",u.b);
}else{
printf("当前系统是大端序(Big - endian)\n");
}
return 0;
}
(5)转换说明
(6)转义字符
在利用printf函数输出字符串时,用户可能需要输出一些转义字符 ‘\n’以及’\t’是比较常用的。
注意有的时候是需要把一些特殊字符当做普通字符输出,比较 “” ,可以把 \” 和 \” 输出
(7)输出结果
重点:返回的是打印的字符个数(不包括 \0 ,因为输出遇到 \0 就结束了)
- Quesion1:分析程序,答出程序的运行效果
#include <stdio.h>
int main()
{
int i = 43;
printf("%d\n",printf("%d",printf("%d",i)));
return 0;
}
- Answer1:
最内层 printf("%d", i) 输出 43,该函数返回值为输出字符数 2(字符 4 和 3 共 2 个)
中间 printf("%d", ...) 接收内层返回值 2,输出 2,此函数返回值为 1(字符 2 共 1 个)
最外层 printf("%d\n", ...) 接收中间返回值 1,输出 1 并换行
最终输出结果为:
4321(换行符也会生效,实际显示为 4321\n,但通常表现为下一行开始新内容)
Quesion2:程序为什么是下面的运行效果?回答:从计算机组成原理的角度,工作效率问题
Answer2:
这是标准输出缓冲机制导致的差异:
左图程序:printf("hello") 无换行符 \n,输出先存入缓冲区,未立即显示。程序执行 sleep(5) 时,缓冲区未刷新,直到程序结束前缓冲区内容才被刷新输出,接着执行后续代码输出 world,最终 “hello” 和 “world” 一起显示
右图程序:printf("hello\n") 含换行符 \n,会触发缓冲区刷新,“hello” 立即显示。执行 sleep(5) 延时后,再输出 “world\n”,“world” 也正常显示
计算机的CPU的性能好,所以CPU的执行效率高,但是计算机的输入设备和输出设备的效率低,所以CPU为了提高工作效率,要降低访问输入输出设备的次数
根据IO设备的不同,可以把缓冲区分为输入缓冲区和输出缓冲区,同样,根据刷新形式的不同,可以把缓冲区分为三种:全缓冲、行缓冲、无缓冲
全缓冲:指的是当缓冲区被填满就立即把数据冲刷到文件、或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件,一般读写文件的时候会采用
无缓冲:指的是没有缓冲区,直接输出,一般linux系统的标准出错stderr就是采用无缓冲,这样可以把错误信息直接输出
行缓冲:指的是当缓冲区被填满(一般缓冲区为4KB,就是4096字节)或者缓冲区中遇到换行符’\n’时,或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件中,一般操作IO设备时会采用,比如printf函数就是采用行缓冲
- Quesion3:
如果在使用printf函数输出字符串的时候忘了添加结束标志’\n’,并且没打算输出太多字符,请问此时要输出的字符串会不会输出?
- Answer3:
可以输出,当程序结束时会自动刷新缓冲区,如果输入缓存区中存在数据则被丢弃,如果输出缓存区存在数据,则会输出
- Quesion4:
如果在使用printf函数输出字符串的时候忘了添加结束标志’\n’,并且没打算输出太多字符,并且程序永远不退出,请问此时要输出的字符串会不会输出?
- Answer4:
不会输出,因为不满足刷新条件
注意:用户可以选择手动刷新缓冲区,可以调用标准C库中的fflush()函数接口
注意:不要利用fflush函数区刷新输入缓冲区(stdin) ,因为一般编译器是不支持该操作!!!
Quesion5:查阅资料,了解输入输出缓存区为什么一般是4KB大小? 提示:涉及到linux的内存分页机制
Answer5:
首先,Linux 内存管理的分页机制,传统页大小是 4KB。缓冲区设为 4KB,读写时能与内存页匹配,减少 IO 次数。每次读写一个页大小,提升效率,减少系统调用开销。另外,硬件和系统设计的兼容性,4KB 是常见页大小,缓冲区适配这个尺寸,让数据传输更高效,减少碎片,优化缓存利用。现在组织这些点,形成清晰解析。
在 Linux 中,输入输出缓冲区设为 4KB 主要与内存分页机制相关:
1.内存页大小适配:Linux 传统内存页大小为 4KB,缓冲区大小与内存页对齐,可减少 I/O 操作次数。程序读写数据时,按 4KB 块操作,一次读写刚好对应一个内存页,提升数据传输效率。
2.降低系统调用开销:若缓冲区过小,需频繁触发系统调用完成数据读写;4KB 大小减少了调用次数,降低 CPU 与磁盘交互的开销。
3.硬件与系统协同优化:4KB 是硬件和操作系统长期协同的结果,适配磁盘等设备的读写单元,减少数据碎片化,让缓存机制更高效利用内存空间。
格式化输入
用户会使用标准输入设备(键盘)写入数据,所以标准C库提供了一个输入函数scanf(),作用是把数据流写入到内存中
- scanf指的是scan format(格式化扫描),用法类似于printf函数
注意:如果在scanf的字符串中使用了多个转换说明符,则输入的数据类型必须个转换类型一一对应