我这个萌新在HDOJ上的被A+B problem虐记录

在六天前,也就是10月21日,我受某位学长怂恿去了HDOJ看看,结果被虐的不要不要的。我当时连while语句都不知道诶,英语也不好,连题干都看不懂,

题是这样的

Problem Description
Calculate A + B.
Input
Each line will contain two integers A and B. Process to end of file.
Output
For each case, output A + B in one line.
Sample Input
1 1
Sample Output
2

第一遍是我还真的以为是简单的算一遍A+B呢,当然没有通过啦。有位同级大佬告诉我不是算一次就行的,还要接着算。直到今天(27日)才看到开头有一个EACH,好吧,真实人生一大悲哀。

可以AC的代码是这样的

#include<stdio.h>
int main()
{
	int a,b;
	while(scanf("%d%d",&a,&b)!=EOF)
	{
		printf("%d\n",a+b);
	}
	return 0; 
}

那么scanf后为什么非要加一个!=EOF呢?

下面这篇博文和学长大佬都说ACM是靠导入文件读取数据的,EOF表示文件尾,读取到就跳出循环,不加EOF没有这个效果
从while(scanf() != EOF)说到ACM OnlineJuge的评判原理.

《C Primer Plus中文第六版》220页提到,C的输入函数内置了文件结尾检测器
检测文件结尾的一种方法是:在文件末尾放一个特殊的字符标记文件结尾,例如内嵌的Ctrl+Z字符。
另一种方法是:存储文件大小的信息。如果文件有3000字节,读到3000字节时便达到文件末尾。
读取到文件末尾时,getchar()和scanf()会返回一个特殊值EOF(end of file缩写),EOF定义在stdio.h中:#define EOF (-1)

所以,while(scanf("%d%d",&a,&b)!=EOF)加上!=EOF的作用就是,读取到文件末尾时可以跳出循环。如果不加,无法跳出while(),系统判定超时。

接下来是一些题外话?

之前总有一种执念,以为scanf的返回值为-1或0时自动跳出while(),现在想想真是脑袋被门夹了,捂脸捂脸~

scanf()返回类型为int,返回值为成功读取的项数
读取失败时返回值为0
检测到文件末尾或硬件问题时返回值为-1

使用%d转换说明读取时,scanf()每次读取一个字符,跳过所有空白符,直至遇到第一个非空白字符才开始读取,如果找到一个数字或符号(+/-),便开始读取保存字符,直至遇到非数字字符。
如果第一个非空白符不是数字,scanf()停在那里,把字符放入输入中,不会把值赋给指定变量。在下一次读取时,首先读取到的还是该字符,scanf()始终无法越过该字符,且此时scanf()返回值为0,上面的代码就是死循环……

我是用下面这个代码研究返回值的

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
	int a;
	int b;
	int c;
	printf("请输入三个整数\n");
	int x=scanf("%d%d%d",&a,&b,&c);
	
	printf("a=%d\nb=%d\nc=%d\n返回值=%d\n",a,b,c,x);
	system("pause");
	
	return 0;
}

别问我为什么不用上面AC的代码修改,我不会TAT,求大佬教我啊

经过以上研究,代码改成下面这样也可以AC了,反正题干一组两个整数,顺便解决了输入非数字会造成死循环的问题……

#include<stdio.h>
int main()
{
	int a,b;
	while(scanf("%d%d",&a,&b)==2)
	{
		printf("%d\n",a+b);
	}
	return 0; 
}

其他人的AC代码还有写成这样的
就是把
while(scanf("%d %d",&a,&b)!=EOF)
改成
while(~scanf("%d %d",&a,&b))

没有丧偶的博文while(~scanf("%d %d",&a,&b))和while(scanf("%d %d",&a,&b)!=EOF)
原来他也改成scanf("%d %d",&a,&b)==2了……不过我真的是自己想出来的

【解释】while(~scanf("%d", &n))的~的含义

“~”意为取一个数的二进制反码。
while里不加判断条件默认为语句不等于0……
那么就是说取反后不等于0就进行循环

当scanf()读到文件结束,返回值为-1。
按教材写的“正数的补码、反码都是其本身
负数取反:原码的符号位不变,其他位按位取反”。

(-1)10原码(10000001)2取反后为八位二进制数 (11111110)2?并没有看出来这个是0

于是机智的我又写了一段程序

#include<stdio.h>
int main(void)
{
	int A=-1;
	int B=999; 
	B=~A; 
	printf("%d",B);
	
	return 0;
}

输出结果为0……我先去哭一会……

接下来转到这位大神的博文

才知道二进制数在内存中是以补码的形式存放的,-1原码即为:
(1111,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0001)2
反码:
(1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1110)2
补码:
(1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111)2
按位取反:
(0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000)2
所以-1取反为0

………………………………于是下面这段可以不看了………………………………
0 的机器骂(原码):0|0000000000000000000000000000000
-1的机器码(补码):1|1111111111111111111111111111111
怎么又蹦出来个补码?1和0字符宽度不一样,没对齐强迫症忍耐一下……

来自于百度知道,没找到-1取反,先拿这个将就吧。和我们教材上写的根本不一样,教材说符号位不变……
………………………………于是上面这段可以不看了………………………………
所以while(~scanf("%d %d",&a,&b))与while(scanf("%d %d",&a,&b)!=EOF)等价
都是在scanf()返回值为-1时跳出循环

//…………………………分割线…………………………

累啊~~~问题还有很多(还是缓冲区不懂),取反不会,先在这里记着吧,不写下来怕忘了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值