竞赛中应该用scanf还是cin? scanf&printf与cin&cout的比较+快读快写

前言
相信不少C++初学者对于在算法竞赛中输入输出应该用C++的cin&cout,还是用C风格的scanf&printf感到疑惑,对我而言,我是很喜欢cin&cout简洁但又安全的性质,但是之前也听说过cin&cout的是效率低于scanf&printf的说法,那么在面对大量数据I/O时scanf&printf可能解决cin&cout超时的问题,那么我们只能抛弃cin&cout了吗?关于这个问题的答案,我认为作为理工生,我们应该用严谨的数据来说话。
我利用代码生成了三千万个【1,100】以内的随机数,并且将分别用cinscanf输入到一个数组中。

	const int MAXNUM  = 30000000;
	int RandomNumber;
 	srand((unsigned)time(NULL));
 	for (int i = 0; i < MAXNUM; i++)
	{
		RandomNumber = rand() % 100 + 1;
	}
	int start = clock();
	for (int i = 0; i < MAXNUM; i++)
 	{
 		//scanf("%d ", &number[i]);
 		cin >> number[i];
 	}
 	printf("scanf用时:%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);
	for(int i = 0;i < 100000; i++)
	{
		printf("%d\n", i);
		//cout << i << endl;
	}

我在Windows系统GCC编译器下得出的用时数据(单位:秒),且已验证过不存在偶然性:

scanfcin
6.83611.303

同时,我用printf和cout分别输出十万个正整数

printfcout
6.17211.509

于是可以得出结论:cin&cout的确在效率上是低于scanf&printf的(当然在我得出的数据中表明并没有网上流传的差距那么夸张)。

这时又有一个新的问题:为什么有这么大的差距呢?这是因为C++为了和C保持同步、在混用 printf&scanf 和 cin&cout时的时候不发生混乱,将它的输入/输出流绑到了一起。
而我们可以通过std::ios::sync_with_stdio(false);指令关闭同步(这里要注意:当关闭同步之后为了确保准确,不要使用 printf&scanf了),除此之外我们还可以通过std::cin.tie(nullptr);获取cin更优的性能(解除std :: cin和std :: cout之间的绑定,来降低IO的负担使效率提升)。那么在对iostream优化之后的 printf&scanf 和 cin&cout效率差距又有多大呢?我通过上述的实验得出以下数据:

scanfcin(优化后)
6.8362.695
printfcout (优化后)
6.1726.178

可以看见在关闭同步之后cin的效率已经是高于scanf了,并且cout的速度与printf的速度也相差无几,那我们还能不能继续优化呢?
我们注意到通常在用cout输出的时候,更习惯去使用endl,它既可以达到换行的需求又可以刷新缓冲区,然而在如此高度的循环下它一直对缓冲区的操作却降低了效率,所以将endl换成’\n’cout的效率将会起飞(顺带一提,我在开启同步的条件发现endl或是\n对cout效率的影响并不大,不知道是不是我的问题,希望各位指出)。

printfcout(‘\n’)
6.1721.106

如此一来我们再一次得出结论:在同步开启时,scanf&printf的效率要高于cin&cout;当同步关闭时,cin&cout的效率要高于scanf&printf。
当然我也看过一些帖子中表述在(ACM)关闭同步后cin&cout依旧会超时,而换成scanf&printf不会,我认为这是算法的效率问题,提升自己算法的效率更加重要。事实上在一些场景中,scanf&printf或是cin&cout的使用不应该是刻板的,应该择优选择(尤其是遇到一些要求格式输入输出的题目,scanf&printf还是很香的)。

当然为了预防当scanf&printf也会超时的情况,这里再给出一个C++下快读快写的一个模板,毕竟快读快写的效率真的很高。

	/*快读快写*/
	template <typename T>
	inline T read()//这里加inline是为了解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题
	{
	   //自定义的类型T
	   register T sum = 0, f1 = 1;//f1是标志位
	   register int ch = getchar();
	   //如果输入的是负数
	   for(; !isdigit(ch); ch = getchar())
	   {
	       if(ch == '-')
	       {
	           f1 = -1;
	       }
	   }
	   for(; isdigit(ch); ch = getchar())
	   {
	       sum = sum * 10 + ch - '0';
	   }
	   return sum * f1;
	}
	//int a = read<int>();
	template <typename T>
	inline void write(T x)
	{
	   static int stk[20];//模拟压栈
	   int top = 0;
	   do{
	       stk[top++] = x % 10;
	       x /= 10;
	   }while(x);
	   while(top)
	   {
	       putchar(stk[--top] + '0');
	   }
	}
	//write(a);

这里再附上优化后的cin&cout与快读快写的效率比较:

readcin
0.4852.695
writecout
18.9081.106

总结&补充
至于有些帖子上总结的“数据量超过1e7就用cin&cout,小于就用scanf&printf”的言论我也将MAXNUM调整到到了一百万去测试,我得出的结果是优化之后的cin&cout的效率(0.092s)依旧是高于scanf&printf(0.222s)与通常的cin&cout的(0.374s)。
超过1e7的数据在上文中展示过了,scanf&printf的确是占有优势的,而通过优化之后的cin&cout效率更高,至于到底应该用scanf&printf还是优化之后的cin&cout,我认为还是看个人爱好与题目要求吧(在项目中,方便的话我还是推荐使用cin&cout,因为更安全)。

部分参考自文章

  • 31
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
C语言scanf函数,"&"符号表示取地址操作符。它的作用是获取变量的地址,以便将输入的值存储到该地址对应的变量。在scanf函数,当需要将输入的值存储到一个地址时,不需要使用"&"符号。例如,当scanf函数后面接收的是一个地址时,如scanf("%s",a),a本身就是一个地址,所以不需要加取地址符"&"。而当scanf函数后面接收的是一个实际变量时,需要使用"&"符号。例如,scanf("%d",&a),此时a不表示地址,所以需要加取地址符"&"。这是因为scanf函数需要知道变量的地址才能将输入的值存储到该地址对应的变量。\[1\] \[2\] #### 引用[.reference_title] - *1* [C语言学习小问题:关于scanf函数“&”的使用](https://blog.csdn.net/ningqingzy/article/details/109560469)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c语言 scanf为什么要用&来取地址](https://blog.csdn.net/qq_44017116/article/details/123159536)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [C语言&是什么意思?a&b怎么理解?](https://blog.csdn.net/weixin_42214654/article/details/117103066)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白还在写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值