目录
1.用scanf读取data.txt的全部数字,计算所需时间
1.格式上
1.scanf/printf
1.不能自动识别输入数据的类型,需要手动指定格式(%d,%s,...)字符串,容易出现格式错误.需要确保格式字符串与变量类型匹配,否则会导致未定义行为
2.格式化输出更精确直观,特别适合复杂格式的输入输出
复习
2.cin/cout
cin和cout会根据变量类型自动处理输入输出,避免格式化错误,使用简单
例如:cout默认不会输出六位小数,自动忽略小数点后多余的0,而printf函数打印浮点数的时候,小数点默认打印6位.
★2.性能上
先说结论
***scanf/printf通常比cin/cout快***
演示
先生成存储1~10000000的数字(数据量越大差异越明显)的data.txt,以便于scanf和cin读取
#include <stdio.h>
#include <time.h>
int main()
{
int a = 0;
FILE* pf = fopen("data.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 1;
}
printf("正在写入...\n");
for (int i = 1; i <= 10000000; i++)
{
fprintf(pf, "%d ", i);
}
fclose(pf);
printf("写入成功!");
}
附data.txt的下载链接: https://pan.baidu.com/s/1amRp8C7q7XD0yIv9F8KfBg?pwd=z8dy 提取码: z8dy
1.用scanf读取data.txt的全部数字,计算所需时间
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
int a = 0;
//freopen将stdin重定向到文件,即scanf可以文件中读取数据
FILE* pf = freopen("data.txt", "r",stdin);
if (pf == NULL)
{
perror("fopen");
return 1;
}
clock_t begin = clock();
for (int i = 1; i <= 10000000; i++)
{
scanf("%d", &a);
}
clock_t end = clock();
printf("time=%dms", end - begin);
return 0;
}
运行结果
2.用cin读取data.txt的全部数字,计算所需时间
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
int a = 0;
//freopen将stdin重定向到文件,即scanf可以文件中读取数据
FILE* pf = freopen("data.txt", "r",stdin);
if (pf == NULL)
{
perror("fopen");
return 1;
}
clock_t begin = clock();
for (int i = 1; i <= 10000000; i++)
{
cin >> a;
}
clock_t end = clock();
cout<<"time="<< end - begin<<"ms";
return 0;
}
运行结果
简单解释
cin和cout由于要兼容C语言的输入和输出,封装实现较复杂,通常比scanf/printf慢
举例
https://ac.nowcoder.com/acm/problem/227585
本题的输入描述中有这样两个需要重点关注的地方:
1.1≤T≤1000000 T最大可以到100万,数据量很大!!!
2.请选择较快的读入方式
显然需要用scanf/printf来输入和输出数据
详细解释性能上的差异
要想深刻了解cin需要看官方网站对此的解释:https://legacy.cplusplus.com/reference/iostream/cin/
背景知识
1.在 C++ 中,标准输入输出流(如cin和cout)由C++ 标准库提供,而在 C 语言中,标准输入输出函数(如scanf和printf)由C标准库提供. C++ 从C发展而来,因此C++ 标准库的输入输出流系统需要与 C 标准库的输入输出系统兼容来确保在同一程序中能够混合使用C和C++的输入输出函数
为了兼容,C++标准库默认会将cin 、cou等C++ 流对象与stdin、stdout等C标准库的流对
象同步在一起.这种同步操作意味着每次使用 cin 或 cout 时,都会自动刷新 C 标准
库的缓冲区,确保C++和C的I/O一致
2. 默认情况,cin 和 cout 之间存在一种绑定关系,即每当从cin 读取数据时,任何之前通过cout输出的内容都会被强制刷新到屏幕上(这个影响性能,后面会解释).这个机制保证了输出内容能够立即显示给用户,这对于交互式程序非常有用.但这种绑定也可能导致性能问题,特别是在需要频繁读取大量数据的情况下.这是因为每次从cin读取数据都会触发一次输出缓冲区的刷新,即使实际上没有进行输出操作,也会浪费时间
重点强调影响性能的两句话
cin is tied to the standard output stream cout (see ios::tie)
By default, cin is synchronized with stdin (see ios_base::sync_with_stdio).
1.cin与标准输出流cout绑定(be tied to) 2.默认情况,cin与stdin同步(synchronized v.同步)
显然可以得出:如果想优化cin/cout的性能,有两个解决方法:1.解除同步 2.取消绑定
3.提高cin/cout的性能的两个方法
1.解除同步
使用以下代码可以关闭C++标准库与C标准库之间的I/O同步,不再自动刷新C标准库的缓冲区,cin和cout独立进行,提高性能
//为false即关闭同步,前面为ios_base为取消与C风格的I/O的同步`
ios::sync_with_stdio(false);//在ios作用域内使用sync_with_stdio函数
(这里的sync_with_stdio全称: synchronized_with_standard_input_output)
注意:只有在只使用cin/cout进行 I/O,而不涉及C的I/O函数的情况下才是安全的!!! 1.如果与C的I/O函数混合使用可能导致不可预期的行为,如输出顺序错乱.2.I/O 操作可能不再是线程安全的,特别是在多线程环境中需要谨慎使用
2.取消绑定
使用以下代码可以解除标准输入流cin与标准输出流cout之间的默认绑定,当从 cin 读取数据时,cout 的缓冲区就不会被刷新(竞赛题要的只是最终结果,输出不需要实时显示给用户),当程序需要同时处理多个输入输出流时,解除绑定有助于减少同步带来的延迟,提高性能
cin.tie(0);
cout.tie(0);
或者写成
cin.tie(nullptr);
cout.tie(nullptr);
3.模版
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//do_something
★特别提醒:解除同步和取消绑定后不能与C的I/O函数(如scanf和printf)混合使用,会不安全!
4.测试加上后再读取1~10000000个数字所需要的时间
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int a = 0;
FILE* pf = freopen("data.txt", "r",stdin);
if (pf == NULL)
{
perror("fopen");
return 1;
}
clock_t begin = clock();
for (int i = 1; i <= 10000000; i++)
{
cin >> a;
}
clock_t end = clock();
cout<<"time="<< end - begin<<"ms";
return 0;
}
运行结果
性能显著提升!甚至比scasnf的性能要好!
4.总结
如果输入的数据量比较小(10^6 以内),使用cin和cout或scanf和printf
但是如果输入的数据量比较大(10^9 左右),推荐使用scanf和printf或优化后的cin和cout
注:一些极端情况下,当输入输出的规模非常大的时候,scanf和printf也不能满足,会使用快速读写的方式,之后会讲