使用联合是干净易懂的
union
{
float f;
unsigned int ul;
unsigned char uc[4];
} myfloatun;
myfloatun.f=somenum;
printf("0x%08X\n",myfloatun.ul);
从编译器的角度来看,比指针更安全。Memcpy工作正常。
编辑
好吧,好吧,这里有完全有效的例子。是的,如果你不注意编译器如何分配联合并填充或对齐它,它可能会出问题,这就是为什么一些/许多人说这种方式使用联合是危险的原因。然而,其他选择被认为是安全的吗?
阅读一些关于C ++的内容,它自己也存在与联合相关的问题,并且联合可能根本无法工作。如果您真的指的是C++而不是C,则这可能是不好的。如果你说了“Kleenex”并指的是纸巾,那么这可能有效。
#include
#include
#include
typedef union
{
float f;
unsigned char uc[4];
} FUN;
void charRepresentation ( unsigned char *uc, float f)
{
FUN fun;
fun.f=f;
uc[0]=fun.uc[3];
uc[1]=fun.uc[2];
uc[2]=fun.uc[1];
uc[3]=fun.uc[0];
}
void floatRepresentation ( unsigned char *uc, float *f )
{
FUN fun;
fun.uc[3]=uc[0];
fun.uc[2]=uc[1];
fun.uc[1]=uc[2];
fun.uc[0]=uc[3];
*f=fun.f;
}
int main()
{
unsigned int ra;
float test;
char result[4];
FUN fun;
if(sizeof(fun)!=4)
{
printf("It aint gonna work!\n");
return(1);
}
test = 4.7567F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 1.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 2.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 3.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.15625F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
result[0]=0x3E;
result[1]=0xAA;
result[2]=0xAA;
result[3]=0xAB;
floatRepresentation(result,&test);
printf("%f\n",test);
return 0;
}
输出的结果看起来像这样
gcc fun.c -o fun
./fun
0x40 0x98 0x36 0xE3
0x3F 0x80 0x00 0x00
0x40 0x00 0x00 0x00
0x40 0x40 0x00 0x00
0x00 0x00 0x00 0x00
0x3E 0x20 0x00 0x00
0.333333
您可以通过手动验证,或查看此网站,因为我直接从中取出了示例,输出与预期相匹配。
http://en.wikipedia.org/wiki/Single_precision
编程相关内容:你永远不应该使用指针指向内存并用不同类型查看它。我从来没有理解为什么这种做法经常被使用,特别是在结构体中。
int broken_code ( void )
{
float test;
unsigned char *result
test = 4.567;
result=(unsigned char *)&test
//在此处对结果进行操作
test = 1.2345;
//在此处对结果进行操作
return 0;
}
这段代码99%的时间都能正常工作,但不是100%的时间。当你最不希望它失败并且在最糟糕的时候,比如你最重要的客户收到它的第二天,它就会失败。优化器会在这种编码风格下吃掉你的午餐。是的,我知道大多数人都这样做,并且被教导这样做,也许从未受过伤...但这只会让它最终发生时更加痛苦,因为现在你知道它可能会失败(使用像gcc这样的流行编译器,在像个人电脑这样的常见计算机上)。
在使用此方法测试fpu时,程序化构建特定的浮点数/模式时,我看到了这种失败情况,我转而采用联合方法,到目前为止从未失败过。按定义,联合中的元素共享相同的存储块,编译器和优化器不会混淆该共享存储块中的两个项目...在同一共享存储块中。使用上述代码时,您依赖于一个假设,即每个变量的使用背后都有非寄存器内存存储,并且在下一行代码之前将所有变量写回该存储中。如果您从不进行优化或使用调试器,则可以正常运行。在这种情况下,优化器不知道结果和测试共享相同的内存块,这是问题/错误的根源。要进行指针游戏,您必须开始在所有内容上放置volatile,就像联合一样,您仍然必须了解编译器的对齐和填充方式,您仍然必须处理端点。
问题是通用的,编译器不知道这两个项目共享同一内存空间。对于上面的特定琐碎示例,我观察到编译器优化了将数字赋值给浮点变量的过程,因为该值/变量从未被使用。该变量的存储地址被使用,如果您要printf *result数据,则编译器不会优化掉result指针,因此不会优化掉测试地址,也不会优化掉test的存储,但在这个简单的示例中,数字4.567和1.2345可能永远不会进入编译后的程序。我还看到编译器分配了test的存储空间,但将数字分配给浮点寄存器,然后从未使用该寄存器,也没有将该寄存器的内容复制到已分配的存储空间中。它失败的原因对于更不琐碎的示例可能更难以理解,通常与寄存器分配和驱逐有关,改变一行代码就可以使其正常工作,改变另一行代码就会导致错误。
Memcpy
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void charRepresentation ( unsigned char *uc, float *f)
{
memcpy(uc,f,4);
}
void floatRepresentation ( unsigned char *uc, float *f )
{
memcpy(f,uc,4);
}
int main()
{
unsigned int ra;
float test;
unsigned char result[4];
ra=0;
if(sizeof(test)!=4) ra++;
if(sizeof(result)!=4) ra++;
if(ra)
{
printf("这不会起作用\n");
return(1);
}
test = 4.7567F;
charRepresentation(result,&test);
printf("0x%02X ",(unsigned char)result[3]);
printf("0x%02X ",(unsigned char)result[2]);
printf("0x%02X ",(unsigned char)result[1]);
printf("0x%02X\n",(unsigned char)result[0]);
test = 0.15625F;
charRepresentation(result,&test);
printf("0x%02X ",(unsigned char)result[3]);
printf("0x%02X ",(unsigned char)result[2]);
printf("0x%02X ",(unsigned char)result[1]);
printf("0x%02X\n",(unsigned char)result[0]);
result[3]=0x3E;
result[2]=0xAA;
result[1]=0xAA;
result[0]=0xAB;
floatRepresentation(result,&test);
printf("%f\n",test);
return 0;
}
gcc fcopy.c -o fcopy
./fcopy
0x40 0x98 0x36 0xE3
0x3E 0x20 0x00 0x00
0.333333
根据我的上述评论,我可能会引起争议,取决于你选择站在哪一边。也许memcpy是您最安全的路线。您仍然必须非常了解编译器,并管理好字节序。编译器不应该搞砸memcpy,它应该在调用之前将寄存器存储到内存中,并按顺序执行。
result
包含什么值? - James McNellisresult
做什么? - sigfpe