本文适合有一定基础的新手,建议用于回忆复习,简单的知识点不再做解释。
错误示例:
float fnum;
fnum = 1.1;
printf("%d %d",2.2);
此处想要输出1.1和2.2,输出结果显然与期望不符。
1.转化说明(格式声明)与参数不符
此处的3.1是float型,转换说明却是%d。
在程序中,数据的存储方式是固定的,例如浮点型通常由32位组成,8位用于表示指数的值和符号,剩下的24位用于表示非指数部分,而int类型则是16位,1位表示正负号,其余表示整数的值。
当然C标准只规定了最小最大存储位数,因此此处只是举例,并不是任意系统的真实存储方式。
数据在电脑中的存储方式永远是0,1组成的有序数列,转换说明不会更改数据的存储方式,只是起到了翻译数据的作用,因此,当转换说明与数据的存储方式不同时,会输出错误的结果。
例如:
char a;
a = '65';
a是字符型,在电脑中以整数存储:1000001(注意:实际储存会经历由原码到反码再到补码的过程,但补码不便阅读,此处用的原码)
用%c,电脑会以字符型输出。
使用%i,电脑会先把二进制转换为十进制,再输出。
同理,对于整数若使用无符号的转换说明,电脑会将第一位识别位有效数字而非符号表达。
也正是因为如此,你不可能用转换字符去截断数据,例如不可能使用%2d去截断123,得到23,当最大字宽超过2,依旧会原样输出。此外,异常截断的说明会在文末。
值得注意的是,默认情况下,类似3.1这样未经声明直接使用的常数会被储存为double类,相较于float会拖累程序的运行速度,想要使用float保存,应当在浮点数后添加f。
例如:
2.1f*3.1f的计算速度会快于2.1*3.1
更改后依然错误的示例:
float fnum;
fnum = 1.1;
printf("%f %f",2.2f);
2.转换声明(格式声明)应与待打印参数一一对应。
更正后的示例:
float fnum;
fnum = 1.1;
printf("%f %f",fnum,2.2f);
常见的参数传递形式是通过栈来实现的,栈有以下特点,单进单出,先进后出。参数传递从栈顶开始,通过转换说明将储存的数据提取出来,但实际操作中,转换说明在栈底对齐,因此,即使靠后解释说明正确,也会受先前的转换说明的影响,造成栈中数据的解释错位。
截断
对于多位储存格式向少位存储格式转换会发生截断。
例如:
char a;
a = 1111;
printf("%c",a);
a的值明显溢出,但截断后的a不会是11,这是因为数据在电脑中以二进制储存。
对于截断后的数据,统一的计算方式为模以二的存储位长次方,例如char的存储为一字节八位,因此1111模以2的8次方为截断后的数据,同理int的存储为四字节三十二位,因此应模以2的多少次方呢?
答案是2的32次方,但数据超过2的31次方减1时会先占用符号位,此时数据已经异常了,符号位由零变1数据变负数。
例如:
2147483648结果为-2147483648
4294967297的结果为1
总之,理解了数据在电脑中的存储,转换和计算步骤,就能通过严谨的推导得到结果。