第二章 初识 I/O 函数
2.1 前导程序
// meet_IO.c
#include <stdio.h> // std是标准 io是输入输出
int main()
{
int a=0;
unsigned b = -1;
char c = 'z';
float f = 2024.9;
printf("a=%d\t unsigned b=%u\t c=%c\t %.1f\n",
a, b, c, f);
return 0;
}
程序输出如下:
a=0 unsigned b=-1 c=z 2024.9
2.2 转换说明
转换说明 | 输出 |
---|---|
%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 | 无符号十六进制数 |
%% | 打印一个 百分号 |
像 %d 这样的字符搭配叫做 转换说明 ,当使用类似于 printf()这样的函数时,一定要使用与 要打印的变量类型相对应 的类型说明,否则结果将可能不是开发者预期的效果
注:虽然转换说明有很多,但是不必全都记下来,只要知道有什么能转换就可以了,需要用到时再回来找需要用的,等用的多了,自然就记住了
2.2.1 程序解释 - 转换说明
printf("a=%d\t unsigned b=%u\t c=%c\t %.1f\n", a, b, c, f);
看代码,a、b、c、f与上面的转换说明都一一对应,然后 printf() 函数把它们的值都写在了对应的位置上,比如a的值就打印在 %d 的位置上
2.2.2 转义字符
读者会很在意上面的 ‘\t’ 和 ‘\n’ 是什么意思?为什么printf()没把它们打印出来?
这种 ‘\t’ 这样的字符称为转义字符,虽然它实际由两个字符’’ ‘t’ 组成,C语言也会把它看成是一个字符,转义的时机是出现 ‘’ 的时候,所以也可称这个反斜杠 为 转义符
转义字符用于在字符串中表示那些难以直接输入的字符或具有特殊功能的字符
常用转义字符:
转义序列 | 含义 |
---|---|
\a | 警报(ANSI C)(这个需要看系统有没有实现,因此未必所有系统都会有警报声) |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车(鼠标光标回到行首) |
\t | 水平制表符(tab键,一般是4个空白字符) |
\v | 垂直制表符 |
\\ | 反斜杠(\) |
’ | 单引号 |
" | 双引号 |
? | 问号 |
\0oo | 八进制值 (详看ASCII编码) |
\xhh | 十六进制值 (详看ASCII编码表) |
// 对于 八进制值 和 十六进制值 举例
char c = '0'; // 0 的 ASCII 码是48
char oc = '\060'; // 48 的 八进制值 是 060
char xc = '\x30'; // 48 的 十六进制 是 0x30
printf("c=%c oc=%c xc=%c\n", c, oc, xc);
看前面的代码:
printf("a=%d\t unsigned b=%u\t c=%c\t %.1f\n", a, b, c, f);
C把三个’\t’换成三个 制表符 ,一个’\n’ 换成 一个换行符,因此它们四个值打印出来后隔得比较远,而且还换了行
2.2.4 转换说明修饰符
看上面的代码:
printf("a=%d\t unsigned b=%u\t c=%c\t %.1f\n", a, b, c, f);
在 %.1f 里的 .1 就是一个修饰符,在这里的意思是 控制浮点数精度,表示打印到小数点后 1 位数字
下面是修饰符表:
修饰符 | 含义 |
---|---|
标记 | 将在下面介绍(-、+、空格、# 和 0),可以不使用或使用多个标记 |
数字 | 最小字段宽度 如果该字段不能容纳待打印的数字或字符串,系统会使用更宽的字段 |
.数字 | 控制精度 对于%e、%E 和 %f 转换,表示小数点右边数字的位数 对于%g 和 %G 转换,表示有效数字的最大位数 对于 %s 转换,表示待打印字符的 最大位数 对于整型转换,表示待打印数字的最小位数 如有必要,使用前导 0 来达到这个位数 只使用 . 表示其后跟随一个 0,所以 %.f 与 %.0f 相同 |
h | 和整型转换说明一起使用,表示 short int 类型的值 示例:“%hu”、“%hd”、"hx"等 总之是为了给 short 类型使用的,虽然它也可以直接使用 “%d” |
hh | 和整型转换说明一起使用,表示 char 的值 示例: “%hhu” “%hhd” |
j | 和整型转换说明一起使用目标是intmanx_t 或 uintmax_t 类型的值, 这些类型定义在 stdint.h 中示例:“%jd” “%jx” |
l(小写L) | 和整型转换说明符一起使用,表示 long 类型的值 (包括 signed long 和 unsigned long) “%ld” “%lu” |
ll(小写L) | 和整型转换说明符一起使用,表示long long int 类型的值 示例:“%lld” “%llu” |
L | 和浮点数转换说明一起使用,表示 long double 类型的值 示例: “%Lf” “%Le” |
t | 和整型转换说明符一起使用,表示 ptrdiff_t 类型的值 ptrdiff_t 是两个指针差值的类型 示例: “%td” “%12ti” |
z | 和整型转换说明,表示 size_t 类型的值,size_t 是 sizeof 返回的类型 示例: “%zd” “%12zd” |
上面的修饰符都是要配合 转换说明 一起使用的,切记一定要使用 与 参数类型 相对应的转换说明
注:如果上面有些修饰符看不懂的也没关系,稍后你可以试一下,如果试了还不知道有什么作用也没关系,可以先不用,等以后有了这样的需求的时候你可能就懂有什么作用了。因此,切记不要硬着头皮逼自己去理解,特别是初学者,以免养成了错误的C观点。
下面是介绍标记 的表格:
标记 | 含义 |
---|---|
- | 待打印项左对齐。即从字段的左侧开始打印该项 示例: “%-20s” |
+ | 打印值的符号,比如:正数的正号 +, 负数的负号 - |
空格 | 有符号值若为正,则显示前导空格,若符号值为负则打印负号标记 |
# | 一般是配合 “%o” 和 “%x” 使用 如果是"%#o",那么打印 代表八进制数的前导 0 如果是"%#x" 则打印代表十六进制数的 0x 还有其他作用《百度》 |
0 | 对于数值格式,用前导 0 代替空格填充字段宽度。对于整数格式,如果出现 - 标记或指定精度,则忽略该标记 示例: “%010d” 和 “%08.3f” |
注:以上各表均来源于 C经典书籍《C Primer Plus》
2.3 printf() 函数
这是一个 变参函数 ,它至少需要一个参数,这个参数是一个字符串(变参函数就是字面意思,不理解以后慢慢就会懂得)
如下:
// 只有一个参数
printf("hello");
// 不定参数
printf("%c%c%s", ' ', 'w', "orld!");
默认情况下,printf() 会把字符打印到 标准输出流,一般情况下标准输出流是屏幕终端
C 语言的 printf()函数的参数传递
2.3.1 printf() 的返回值
大部分函数都有一个返回值,返回值的作用是 让 被调函数 与 主调程序 进行通信。比如:在我们 main 函数中的 return 0; 就是一个返回值,只不过它的作用是进程间通信 (不要求现在理解进程)
而 printf() 函数也有一个返回值,它的返回值表示 打印字符的个数,如果有输出错误,则返回一个负值
int n = printf("hello world!");
// 右对齐 最小占5个字段宽度
printf("%5d\n";)
输出结果:
hello world! 12
2.3.2 printf() 函数的参数传递
程序把传入的值放在 栈区 自行《百度》 此概念对于初学者不要求,也可以尝试去理解,但是理解不了就不用管了,以后学得多了就懂了
2.4 scanf() 函数
printf() 是一个打印输出的函数,那么scanf() 自然就是一个读取函数,它就是 I/O 中 的 I (input)
int a;
// 从标准输入中读取值并保存至 a 中
scanf("%d", &a);
注意:要把 scanf() 中传入的变量名的前面加一个 & 这样的符号,这叫 取地址符,暂时不需要知道为什么要加这个取地址符,你只需要记住一定要加这个符号就够了
好了,现在读者就去写一个类似的 练习再回来看吧。可以尝试使用别的转换说明
scanf() 的使用也和 printf() 相似,需要传入一个字符串,字符串中有转换说明,不过 scanf() 的字符串中包含的字符格式会更严格
比如:
unsigned int a = 0;
// printf()
printf("这个数 a = %u\n", a); // 打印 这个数 a = 0 (换行)
// scanf()
scanf("%u", &a);// 只有与 a 的类型相对应的 转换说明
// 如果这样写
// 那么用户必须 一字不漏的按顺序输入 %d 之前的字符
// 然而即使一字不漏的输入 也无法正确读取到a的值
scanf("输入a的值 %u", &a);
直接输入 10 :
一字不漏的按字符串中的输入:
不管哪种方法都无法正确读取到 10 并赋值给变量 a
scanf() 函数 与 printf() 函数 在转换说明上还有一个区别,如果转换说明与待 读取/输出 的值不对应的话,prinf() 可能还会有别的值输出,而 scanf() 一旦遇到类型不对应,还无法进行转换的,那么这个函数将会直接中断,然后返回到主调程序。而原先未被读取的输入能被下一条 scanf() 输入语句读取,如:
int a=97;
// 期望输出一个字符 参数却是 int 类型
printf("%c\n", a); // 会输出 'a'
// 等待用户输入,且期望读取一个整数 然而用户的输入 却是一个字符 'a'
scanf("%d", &a); // 当读取到 a 时,函数会直接中断,并返回到主调函数
// 第二条输入语句
char c;
// 不需要再次等待用户输入 因为上一次用户输入的字符 'a' 还在,
// 因此会读取到 字符 'a' 给 变量c
scanf("%c", &c);// %c 表示读入一个字符,它能读取任何输入
2.4.1 scanf() 的返回值
scanf() 的返回值是,读取成功的变量个数。如:
int a,b,c;
scanf("%d%d%d",&a, &b, &c);// 如果用户输入了三个整数 120 110 119 那么函数返回值为 3
// 如果用户输入的是 120 c 110 那么返回值为1
可以利用这个返回值判断 scanf() 读取是否成功
2.4.2 scanf() 的注意点
scanf() 中,除了 %c 外的其他转换说明,都会自动跳过待输入值前的所有空白,就是说,除了转换说明 %c 外,其他的转换说明都不会读取 空白 ,这里的空白指的是如:空格、回车键导致的换行符等都属于空白,注意 %c 可以读取所有的字符包括空白
比如:
int a;
// 用户输入为 [回车][tab键][多个空格] 10
// scanf() 会把前面的这些空白丢弃,只读取到 10 这个整数
scanf("%d", &a);
char c;
// 用户输入为 [回车][tab键][多个空格] a
// 这个 scanf() 只读取了 第一个 [回车]
// 其他的输入还留着,可以被下一条输入语句读取
scanf("%c", &c);
另外:
scanf("%d %d");
// 上面的写法的效果与下面的一致 想使用哪种格式看自己喜好
scanf("%d%d");
虽然上面两种写法的效果是一致的,但是它们表达的意义却有些区别:
第一句:%d 表示读取一个整型数字; ’ ’ 两个 %d 中间的空格表示略过空白,读取字符 ;第二个%d表示读取整型数字
第二局:%d表示只读取一个整型数字,第二个%d也表示只读取一个数字;但是呢,为了读取这个数字,scanf() 会 自动跳过第一个字符前的所有空白,注意是第一个字符前的
也就是说 第一行是用户强调略过空白,读取字符,第二行则是因为 scanf() 的机制
那么这个 表示跳过第一个字符前所有空白的空格 能不能与 可以读取一切字符包括空白的 %c 转换说明一起使用呢?
char c;
scanf("%c", &c); // 转换说明前没有空格
printf("c = '%c'\n", c); // 测试输出 C 的值是什么
scanf(" %c", &c);// 注意一定要有 取地址符 结果如何自己试一试吧
printf("c = '%c'\n", c); // 测试输出 C 的值是什么
2.5 printf() 与 scanf() 的 * 修饰符
printf() 与 * 修饰符:
当你要控制字段宽度,但又不想预先指定,希望通过程序来指定时,就可以使用 * 修饰符代替字段宽度
如:
printf("请输入你想要的字段宽度:");
int size = 0;
scanf("%d", &size);
// 在后面的参数中 第一个 size 表示第一个 * 的值,
// 第二个size和第三个size 表示 %*.*f 里的两个 * 的值
// 注意 浮点数转换说明的第一个 * 表示的是最小宽度,第二个 * 表示的是精度,小数点后几位
printf("|%*d|\n|%*.*f|\n", size, 100, size, size, 99.9);
输入:
5
输出:
| 100|
|99.90000|
scanf() 与 * 修饰符:
在scanf() 的 * 也用在 % 与 转换说明之间,作用是跳过相应的输入项
如:
int a=0;
// 下面的意思是跳过两个整数 , 只读取第三个整数
scanf("%*d%*d%d", &a);
表示 %*.*f 里的两个 * 的值
// 注意 浮点数转换说明的第一个 * 表示的是最小宽度,第二个 * 表示的是精度,小数点后几位
printf(“|%d|\n|%.*f|\n”, size, 100, size, size, 99.9);
输入:
5
输出:
| 100|
|99.90000|
###### scanf() 与 * 修饰符:
在scanf() 的 * 也用在 % 与 转换说明之间,作用是跳过相应的输入项
如:
```C
int a=0;
// 下面的意思是跳过两个整数 , 只读取第三个整数
scanf("%*d%*d%d", &a);
练习:在网上 搜文心一言的网站,然后使用这个 ai,让它给你出5道 “只包含printf()与scanf()函数、转换说明知识点的题目” 然后自己练习练习
总结:自己写总结
在文章头可以下载笔记