【C/C++】输入输出(getchar、putchar、scanf、printf、cin、cout讲解)

C/C++中的输入输出函数

在 C/C++中,常用的输入输出函数包括 getcharputcharscanfprintfcincout。这些函数在标准输入输出流(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位小数:
默认输出6位小数
修改为输出2位小数:修改为输出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类型的变量root1root2root3,在输入数据的时候直接给的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,并且将lr数组下标为tmp的地方的值都改成了'f',然后再连续读取 rootl[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;
}

注意:不管是什么类型的数据,都是以字符流的形式输入或者输出的。cincout 不需要手动标明数据类型,它们都能够自动识别数据类型。

cout 的格式控制(使用需要包含<iomanip>)

  1. 设置宽度
    可以使用 setw 设置输出的宽度(以字符为单位)。如果输出的内容小于指定宽度,cout 会用空格填充。
#include <iostream>
#include <iomanip>  // 必须包含这个头文件
using namespace std;

int main() {
    int x = 42;
    cout << setw(10) << x << endl;  // 输出宽度为10,数值42会右对齐
    return 0;
}

输出

    42
  1. 设置浮点数格式
    可以通过在要输出的数字前面加上 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
  1. 设置精度
    单独使用 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
  1. 控制输出的填充字符
    setfill 可以设置填充字符,用于填充宽度不够的空白部分。
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    int x = 42;
    cout << setw(10) << setfill('*') << x << endl;  // 使用 '*' 作为填充字符
    return 0;
}

输出

*******42
  1. 对齐方式
    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
  1. 设置数字的符号
    可以使用 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值