背景
像C语言这种语言对输入输出的要求极其严格,你必须要制定你读取的数据类型以及格式。现在的问题是如果你读取的时候指定了错误的数据类型,会发生什么?
案例分析
加入说你要读取64位无符号整数类型数据,所以你的程序可能大概是这样子的。
1 | unsigned long val=0; |
在这里面有两点需要注意,其一是%lu
,这说明了我要读取的数据类型是long类型且无符号。另一点是变量val
类型的声明一点要一致,否则读取出来的值会被截断。当然你如果读取short
,用int
来存储是不发生任何精度损失的。不过即使如此,这种类型不匹配的方式也是不推荐的。
现在的问题是,如果把程序写成这样会发生什么?
1 | unsigned long val=0; |
我们知道%ld
也是读取long
类型的数据,只不过数据是包含符号的。由于无符号类型的数据的取值范围是$[0,2^{64}-1]$,如果把这个范围分为两份,一部分对于有符号数的正数部分$[0,2^{63}-1]$,另一部分对于与负数部分$[2^{63}-1,2^{64}-1] \rightarrow [-2^{63},-1]$。
对于位于$[0,2^{63}-1]$的数据,在这个例子里面用%ld%
和%lu%
都可以得到相同的结果。但是对于输入位于$[2^{63}-1,2^{64}-1]$的数据,%ld
能正确读取么,它能自动的将这份数据转为负数读取进来么?如果是的话,那么%ld%
读取的值也会是正确的。因为一个负的long
型正数传给unsinged long
类型的变量时,会自动发生强转。
事实上,scanf("%ld",&val)
并不能正确的读取数据。对于$[2^{63}-1,2^{64}-1]$的数字,他将最高的二进制位自动当0处理,读取出来的数值永远在分$[0,2^{63}-1]$之间。
从这个例子里面,我们可以看到C语言对输入输出的要求,在平时写程序的时候一定要注意这些细节。事实上,我们也是因为代码改来改去的,忘记去改输入了才发现的这个问题。