C/C++中的输入输出函数
在 C/C++中,常用的输入输出函数包括 getchar
、putchar
、scanf
、 printf
、cin
和cout
。这些函数在标准输入输出流(stdin 和 stdout)上工作,用于处理字符、字符串、整数和浮点数等数据的输入与输出。
1. getchar
函数
getchar
函数从标准输入流(通常是键盘)读取一个字符,并返回读取到的字符。
相当于使用 cin 或 scanf() 读取一个字符。
用法:
int getchar(void);
使用时要引用头文件 <cstdio>
getchar() 函数返回从键盘输入的⼀个字符的ASCII码值,如果读取失败,则返回 EOF
(即 -1)。使用时不带任何参数。
注意:getchar()
不会忽略空白字符,因为它的本质就是从键盘读取一个字符,空格也算字符(ASCII码值为32)。
#include <cstdio>
int main()
{
int ret = getchar();//输入` x`(空格加x)
putchar(ret);//输出空格,不读到x
return 0;
}
Q:返回值为什么要设计为int而不是char?
A:如果读取失败,返回常量 EOF ,由于 EOF 通常是 -1 ,所以返回值的类型要设为 int ,而不是 char。
Q:怎么模拟读取失败,让 getchar() 返回 -1 ?
A:在输入时按 Ctrl
+ Z
即可。
2. putchar
函数
putchar
函数将一个字符写入标准输出(通常是屏幕)。它接受一个字符参数,并将该字符打印到屏幕上。
用法:
int putchar(int ch);
参数:需要输出的字符(int 类型,通常表示为字符)。
返回值:返回输出的字符(作为 int 类型)或者 EOF(如果输出失败)。
注意:putchar 输出的字符不会自动换行。若需要换行,可以手动输出换行符(‘\n’),换行符也作为一个字符被输出,作用是换行,返回值是换行符的ASCII码值(10)。
这两个函数可以用于快速读写,以后会介绍。
3. printf
函数
printf
函数用于将格式化的数据输出到标准输出(通常是屏幕)。它可以根据格式化字符串来输出各种类型的数据(整数、浮点数、字符、字符串等),并且可以定制占位符的输出格式,功能强大。
注意:printf 调用结束后光标会停在输出结束的地方,并不会自动换行,如果需要换行(将光标移动到下一行的开头)就要在文本末尾加上换行符。
用法:
int printf(const char *format, ...);
format:格式化字符串,指定输出的内容和格式。
后续参数:需要输出的各类数据。
常见占位符
定制输出格式
1.限定占位符的最小宽度
例如:%5d
表示这个占位符的宽度至少为5
,输出的整数宽度至少为5
,如果不满5
位就默认在前面用空格补齐(右对齐)。如果希望改为左对齐,就要加一个负号,即%-5d
。
正常输出:
设定占位符宽度为5,默认右对齐
设定占位符宽度为5,左对齐
2.限定小数位数
当printf
以%f
的格式输出时,默认输出的小数位数是6
,如果想改变输出的小数位数,就要加点符号并在后面写上想要限制的位数。例如想要保留小数点后两位,占位符可以写为%.2f
默认输出6位小数:
修改为输出2位小数:
4. scanf
函数
scanf
函数用于从标准输入流读取数据,并根据格式字符串将其存储到变量中。它应用于从用户获取不同类型的数据(如整数、浮点数、字符串等)。
用法
int scanf(const char *format, ...);
format:格式化字符串,指定输入的数据类型。
后续参数:指定存储输入数据的变量的地址。
返回值:返回成功读取数据的个数。
scanf处理用户输入的原理是:
用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。
占位符
注意:上面所有占位符除了%c
,都会自动忽略输入中的所有空白字符(如空格和换行符),但是%c
不会忽略空白字符,它总是会返回当前的第一个字符,无论是不是空白字符。
如果要强制跳过字符前的空白字符,可以写成 scanf(" %c", &ch) ,这里在%c前面加上了一个空格,表示跳过字符前的所有空白字符。
重点:
1. scanf 会忽略空白字符(空格、换行符和制表符),直到它找到一个有效的输入。
Q:scanf(“%d %d")
和scanf(“%d%d")
有什么区别?
A:没有区别。在两个 %d 之间的空格表示“忽略所有空白字符(包括空格、换行、制表符等),直到读取下一个数字。但是如果两个 %d 之间没有空格,scanf 依然能够正确地读取两个整数,因为它会自动忽略输入中的所有空白字符来解析下一个 %d。
Q:scanf("%d")
和scanf("%d ")
有什么区别?
A:scanf("%d")
会自动忽略前导空白字符,直到遇到第一个非空白字符开始读取整数,到空白字符或者非数字字符(对于%d来讲是无效字符)结束,并且会停止在下一个空白字符处(读到数字之后停止后的第一个空白字符)。如果你输入的是 123,它会成功读取 123,并且跳过后面的空白字符。scanf("%d ")
这个格式字符串也会从标准输入中读取一个整数,但它要求输入后有至少一个空白字符。由于格式字符串最后有一个空格,它告诉 scanf 在读取完整数后,期望继续读取空白字符,如果输入是 123 后没有任何空格或者换行,scanf("%d ")
会阻塞等待输入空白字符,直到用户输入至少一个空格或者换行符。这是一个非常常见的易错点,有时候程序阻塞却找不到原因,就是scanf中占位符后面的这个空格导致的!
2. %s 格式说明符用于读取一个字符串,但它只能读取到第一个空白字符为止。
当输入123 4567时,只将123存入了s中,因为scanf读到空白字符就停止了
int main()
{
char s[10]={0};
scanf("%s",s);//输入123 4567
printf("%s",s);//输出123
return 0;
}
注意: scanf在读取字符串的时候,不会检查读取的字符串的长度是否超过了字符数组的长度,这样一来就容易导致未定义的行为。
可以通过 %9s 格式限定最大输入长度,以防止缓冲区溢出(会导致程序崩溃或未定义行为)。s的长度为10,限制输入9个字符,留一个给’\0’ 。
3. 使用 & 取地址符来将变量传递给 scanf,这是因为 scanf 需要修改这些变量的值,但是如果要存储数据的变量是数组就不需要取地址。
4. scanf 的返回值是成功读取的项数。如果输入失败,返回值可能是 0 或 EOF。
在题目中可以搭配循环用来读取测试用例
5. cin
输入流 和 cout
输出流
cin
是一个全局对象,用于从标准输入流读取数据(通常针对用户键盘输入)。cin
一般与 >>
(流提取运算符)一起使用,用来从用户键盘输入中提取值。
#include <iostream>
using namespace std;
int main() {
int x;
cout << "请输入一个整数: ";
cin >> x; // 从用户输入读取整数
cout << "你输入的整数是: " << x << endl;
return 0;
}
注意:多个cin
在读取数据时是以空白字符分割输入内容的,但是cin
不会读取空白字符,会自动跳过空白字符读取紧接着的有效内容。
cin的关键问题(25.3.3添加)
Q:那么cin的结束标志是什么呢?按下回车cin就结束了吗?
A:回车(Enter)不会单独作为终止标志:
当你按下回车,如果当前的 cin 读取完成,程序会继续执行。
但如果 cin 需要更多输入(如多个 cin >> 变量;),这时如果完成了部分的 cin 输入之后按下回车,cin并不会停止,它还是会等待输入。
举例说明:
#include <iostream>
using namespace std;
//二叉树链式存储(存左右结点)
const int N = 300;
char l[N],r[N];
char root;
int n;
void dfs(char root)
{
cout << root;
if(l[root] != '*') dfs(l[root]);
if(r[root] != '*') dfs(r[root]);
}
int main()
{
cin >> n;
cin >> root;
cin >> l[root] >> r[root];
// cin >> root >> l[root] >> r[root]; cin是以空格为分隔符
for(int i=2;i<=n;i++)
{
char t; cin >> t;
cin >> l[t] >> r[t];
}
dfs(root);
return 0;
}
输入数据为:
6
abc
bdi
cj*
d**
i**
j**
错误想法:如果使用cin >> root >> l[root] >> r[root];
读取数据,就会发现能成功将a
读到root
中,l[root]
和r[root]
都读不到内容,这是因为多个cin
连续读取数据的时候是以空白字符
为分割符号,像abc
这样没有空白字符分割的数据,cin
会以为abc
都是给root
的,但是root
是个char
类型,读不下这么多东西,后面的bc
就废掉了。
即使输入数据不用空白字符分开,cin
可以通过变量类型解析数据来成功读取。
如下图所示,定义了三个char
类型的变量root1
、root2
、root3
,在输入数据的时候直接给的abc
,仍然能正确读取。
那么原因到底是什么呢?因为root
变量没有初始化,root直接定义在全局中,程序确实会自动给它赋值为0,但是这仍然叫做root没有初始化,当cin >> l[root] >> r[root]
的时候,实际上这是不可预测行为,数组 l[root] 和 r[root] 需要索引,而 root 还未初始化。
如下图所示,我将root1
定义在main
函数中,并且将其初始化为'a'
,此时我再执行cin >> root >> l[root] >> r[root];
的操作的时候就能成功将三个数据全部读取到,因为root1
已经初始化过了,这样的操作是安全的。
继续解释,如下图所示,我定义了一个变量tmp = 10
,并且将l
和r
数组下标为tmp
的地方的值都改成了'f'
,然后再连续读取 root
、l[tmp]
、r[tmp]
,这时候的tmp下标是已经定义过了的,所以在读取时没有任何问题,成功的将l[tmp]
、r[tmp]
的值修改成了'b'
、'c'
。
Q:cin 和 cout 能进行连续输入输出吗?
A:可以。使用 cout 进行变量的输出,实质上是将变量插入到 cout 对象里,并以 cout
对象作为返回值返回,因此我们还可以用 << 在后面连续输出多个内容。
cout
是一个全局对象,用于向标准输出流写入数据(通常针对控制台窗口)。cout
一般与 <<
(流插入运算符) 一起使用将数据以字符流的形式打印在终端上。
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl; // 输出字符串
return 0;
}
注意:不管是什么类型的数据,都是以字符流的形式输入或者输出的。cin
和 cout
不需要手动标明数据类型,它们都能够自动识别数据类型。
cout 的格式控制(使用需要包含<iomanip>)
- 设置宽度
可以使用 setw 设置输出的宽度(以字符为单位)。如果输出的内容小于指定宽度,cout 会用空格填充。
#include <iostream>
#include <iomanip> // 必须包含这个头文件
using namespace std;
int main() {
int x = 42;
cout << setw(10) << x << endl; // 输出宽度为10,数值42会右对齐
return 0;
}
输出
42
- 设置浮点数格式
可以通过在要输出的数字前面加上 fixed 和 scientific 控制浮点数的显示格式。
fixed 使得输出的浮点数以固定的小数格式显示,不会变成科学计数法。
scientific 则使其以科学计数法显示。
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double pi = 3.14159265358979;
cout << fixed << setprecision(4) << pi << endl; // 使用定点格式
cout << scientific << setprecision(4) << pi << endl; // 使用科学计数法
return 0;
}
输出
3.1416
3.1416e+00
- 设置精度
单独使用 setprecision 可以控制浮点数输出的有效数字位数。
与 fixed 联合使用可以控制浮点数输出的小数位数。(直接输出一个浮点数默认小数位数是 5 位,用 fixed 修饰后默认输出 6 位小数,用 fixed 和 setprecision 一起修饰可以自由控制小数位数)
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double pi = 3.14159265358979;
cout << setprecision(4) << pi << endl; // 保留4位有效数字
cout << fixed << setprecision(4) << pi << endl; // 保留4位小数
return 0;
}
输出
3.142
3.1416
- 控制输出的填充字符
setfill 可以设置填充字符,用于填充宽度不够的空白部分。
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int x = 42;
cout << setw(10) << setfill('*') << x << endl; // 使用 '*' 作为填充字符
return 0;
}
输出
*******42
- 对齐方式
C++中的输出默认是右对齐的,使用 left 和 right 可以改变对齐方式
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int x = 42;
cout << setw(10) << left << x << endl; // 左对齐输出
cout << setw(10) << right << x << endl; // 右对齐输出
return 0;
}
输出
42
42
- 设置数字的符号
可以使用 showpos 来显示正数的符号,默认情况下,正数不显示符号
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int x = 42;
int y = -42;
cout << showpos << x << endl; // 显示正数的符号
cout << showpos << y << endl; // 显示负数的符号
return 0;
}
输出
+42
-42