下面是我的C代码。当我为了学习格式说明符(尤其是%c
)而玩弄代码时:
#include <stdio.h>
void main()
{
double a = 0.21;
printf("%c", a);
}
字符串
我碰巧遇到了这样的情况,即使我在printf()
函数中为格式说明符%c
传递一个浮点值作为参数,编译器(我的编译器是gcc)也会以某种方式将值0.21
转换为十进制值223
,后者对应于ASCII字符ß
。
对于a = 0.22
,输出为:)
,其ASCII表中的十进制值为29
我在VS code和CLionIDE上运行了代码,但结果都是一样的。现在它让我挠头好几天,我都想不明白。
我想知道值0.21
和0.22
如何转换为十进制的223
和29
,或者它们如何分别对应于ASCII 223和29,即ß
和)
。
由于值0.21,0.22不对应于任何ASCII码,所以我希望程序不打印任何内容。
但根据输出,我认为这可能与二进制文件有关。
二进制中的0.21等于0.0011011100001010001110111011100...
& 223是11011111
二进制的0.22等于0.0011100001010001110101110000101...
& 29是00011101
而且我找不到任何转换模式。
4条答案
按热度按时间v8wbuo2f1#
通过
float
(在本例中自动转换为double
)如果%c
的类型是int
或升级为int
的更小类型并使用该值,则printf
会查找程序将其传递的参数。在您的情况下,该值恰好是223或29,具体取决于在某些情况下,它可能是不同CPU上的其他东西,或者在程序中任何不相关的更改之后,甚至只是在不同的时间或地点.行为是未定义的,你也可能没有输出或程序崩溃(不太可能,但不是不可能)。使用编译器警告来尝试和检测此类问题(gcc -Wall -Wextra -Werror),避免绞尽脑汁试图理解未定义的行为。
dpiehjr42#
我的编译器是GCC
最有可能的是你正在使用X86-64系统。这种架构有单独的浮点和普通registers。当
printf
试图获取%c
的寄存器时,它读取普通寄存器,但, a
设置浮点寄存器。%c
的值与a
无关。最有可能的是,它是main
初始化后剩余的寄存器值,如_start
或environ
。cx6n0qe33#
为%c传递float或double有未定义的行为。
如果我们可以定义“为什么”,那么0.21的结果就不是“未定义的行为”了。
但是我们可以假设
printf
的实现可能是二进制的--将其转换为int
,并且结果值确实取决于CPU arch,result为“ss”。eimct9ow4#
对于
a = 0.22
,输出为:)
,其ASCII表中的十进制值为29
这是不正确的。")"的ASCII码在十进制中是41。在十六进制中是29。41(2916)是用于在
double
中编码0.22
的位的低字节,如下所示。此外,您看到的0.21
的字符""在某些字符编码系统中被编码为225(E116),而225是用于将0.21
编码为double
的比特的低字节。作为一个简单的测试,您可以将
a
设置为0x1p52 + x
,其中x
是从0到255的任何整数,包括0和255。由于double
数字是编码的,0x1p52 + x
将在低字节中产生x
的编码。例如,如果这是您的系统上发生的情况,使用a = 0x1p52 + 88
将打印"X",ASCII码为88的字符。下面是源代码中
0.22
产生的double
如何以最常用于double
的格式表示,IEEE-754 binary64:注意最后一个字节是41,与您看到的")"匹配。当
%c
被错误使用时,可以看到的一个行为是使用了传入参数的一个字节的值。例如,调用例程可能会传递用于编码double
数字的八个字节,但是printf
应该是int
的四个字节,(%c
接受一个int
值,它实际上打印了一个字节)。所以printf
从它期望找到int
的地方接受了四个字节,将这四个字节转换为unsigned char
,并打印结果字符。C标准没有定义这种行为,但它是一个潜在的结果。对于
0.21
,编码的double
的字节是63,202,225,71,174,20,122,225。您报告的是223,而不是225,但我怀疑您的系统使用的字符编码中的""是225,而不是223。例如,code page 852,在E116(225)点有""。现在
double
的字节被当作整数参数是不常见的。它本质上需要在堆栈上传递参数,而不是在处理器寄存器中传递参数,因为今天的处理器通常使用单独的寄存器来处理浮点和整数类型。脚注
1该格式对指数的大小有限制。这里不适用,因此不详细介绍。当指数太高时,转换为
double
会导致无穷大的表示。当指数太低时,有效数不会被归一化,并且使用特殊的指数代码来表示低于正常值。