C中printf()转换不匹配问题
来源C Primer Plus page 208
1./* intconv.c – 一些不匹配的整型转换 */
/* intconv.c -- 一些不匹配的整型转换 */
#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main(void)
{
short num = PAGES;
short mnum = -PAGES;
printf("%hd %hu\n", num, num);
printf("%hd %hu\n", mnum, mnum);/*结果65200将负数在内存中的补码形式以无符号数输出*/
printf("%d %c\n", num, num);
printf("%d %hd %c\n", WORDS,WORDS,WORDS);
getchar();/*方便看结果*/
return 0;
}
/*整形转换,根据转换说明在内存中截取所需部分或者转化成所需形式*/
/*vs2017结果:
336 336
-336 65200
336 P
65618 82 R*/
2./* floatcnv.c – 不匹配的浮点型转换 */
/* floatcnv.c -- 不匹配的浮点型转换 */
#include <stdio.h>
int main(void)
{
float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4);/*float类型的值作为printf()参数时会被
转换成double类型*/
printf("%ld %ld\n", n3, n4);
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
printf("%ld %ld\n", n1, n2);
printf("%ld %ld\n", n1, n3);
printf("%ld %ld\n", n1, n4);
printf("%ld %ld\n", n3, n1);
printf("%ld %ld %ld %ld %ld %ld\n", n1, n2, n3, n4);/*计算机把n1,n2,n3,n4这些变量或常量或表达式(根据数据类型确定存储字节)的值放入被称为栈(stack)的内存区域,然后根据转换说明选取所需字节的数据转换出来*//*最后一个printf的输出结果可以证明*/
getchar();/*方便看结果*/
return 0;
}
/*vs2017运行结果
3.0e+00 3.0e+00 3.1e+46 6.2e-304
2000000000 1234567890
0 1074266112 0 1074266112
0 1074266112
0 1074266112
0 1074266112
2000000000 0
0 1074266112 0 1074266112 2000000000 1234567890*/
详细说明(来源C Primer Plus page211):
第1行输出显示,%e转换说明没有把整数转换成浮点数。考虑一下,如果使用%e转换说明打印n3(long类型)会发生什么情况。首先,%e转换说明让printf()函数认为待打印的值是double类型(本系统中double为8字节)。当printf()查看n3(本系统中是4字节的值)时,除了查看n3的4字节外,还会查看查看n3相邻的4字节,共8字节单元。接着,它将8字节单元中的位组合解释成浮点数(如,把一部分位组合解释成指数)。因此,即使n3的位数正确,根据%e转换说明和%ld转换说明解释出来的值也不同。最终得到的结果是无意义的值。
第1行也说明了前面提到的内容:float类型的值作为printf()参数时会被转换成double类型。在本系统中,float是4字节,但是为了printf()能正确地显示该值,n1被扩成8字节。
第2行输出显示,只要使用正确的转换说明,printf()就可以打印n3和n4。
第3行输出显示,如果printf()语句有其他不匹配的地方,即使用对了转换说明也会生成虚假的结果。用%ld转换说明打印浮点数会失败,但是在这里,用%ld打印long类型的数竟然也失败了!问题出在C如何把信息传递给函数。具体情况因编译器实现而异。“参数传递”框中针对一个有代表性的系统进行了讨论。
参数传递
参数传递机制因实现而异。下面以我们的系统为例,分析参数传递的原理。函数调用如下:
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
该调用告诉计算机把变量n1、n2、、n3和n4的值传递给程序。这是一种常见的参数传递方式。程序把传入的值放入被称为栈(stack)的内存区域。计算机根据变量类型(不是根据转换说明)把这些值放入栈中。因此,n1被储存在栈中,占8字节(float类型被转换成double类型)。同样,n2也在栈中占8字节,而n3和n4在栈中分别占4字节。然后,控制转到printf()函数。该函数根据转换说明(不是根据变量类型)从栈中读取值。%ld转换说明表明printf()应该读取4字节,所以printf()读取栈中的前4字节作为第1个值。这是n1的前半部分,将被解释成一个long类型的整数。根据下一个%ld转换说明,printf()再读取4字节,这是n1的后半部分,将被解释成第2个long类型的整数(见图4.9)。类似地,根据第3个和第4个%ld,printf()读取n2的前半部分和后半部分,并解释成两个long类型的整数。因此,对于n3和n4,虽然用对了转换说明,但printf()还是读错了字节。
float n1; /* 作为double类型传递 */
double n2;
long n3, n4;
...
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);