具体不知道metal,但在普通C语言中,你会想把f和byteArray放在union中
这里有一些示例代码:
#include <stdio.h>
#include <stdint.h>
union float_byte {
float f;
uint8_t byteArray[sizeof(float)];
};
union float_byte u;
void
dotest(float f)
{
u.f = f;
printf("%.6f",u.f);
for (int idx = 0; idx < sizeof(u.byteArray); ++idx)
printf(" %2.2X",u.byteArray[idx]);
printf("\n");
}
int
main(void)
{
dotest(2.1);
dotest(7.6328);
return 0;
}
这是程序输出:
2.100000 66 66 06 40
7.632800 E6 3F F4 40
更新:
即使在今天,从技术上讲,阅读一个不是最后一个写给工会成员的工会成员不仍然是 UB 吗?虽然,这听起来现在得到了实现定义的行为的广泛支持。许多相关问题之一:stackoverflow.com/questions/2310483/… – yano
不,出于多种原因,它不是 UB。
它可能是“实现定义的”行为,但 只是因为 CPU/处理器字节序重新。内存中 [32 位] float 的格式,如果我们希望解释 byteArray 中的字节
但是,AFAICT,这不会影响 OP 的问题,因为这只是为了获得一个字节缓冲区 [用于数据的二进制/序列化?]。
如果想要解释数据(例如设计 DIY F.P. S/W 实现),则必须知道 float 的格式和字节序。这 [可能] 是 IEEE 784 格式和处理器字节序。
但是,使用union 只是得到一个字节指针,没有问题。这不是甚至是“实现定义”的行为。
差不多就是这样:
float my_f = f;
uint8_t *byteArray = (uint8_t *) &my_f;
而且,它之所以起作用,是因为它必须起作用。
另外,union [as used here] 是一个常见的习惯用法,可以追溯到 1970 年代,因此它必须得到支持。
而且,它只是工作 [因为它必须按照设计]。
如果我们有:
void
funcA(float f)
{
u.f = f;
}
void
funcB(void)
{
for (int idx = 0; idx < sizeof(u.byteArray); ++idx)
printf(" %2.2X",u.byteArray[idx]);
printf("\n");
}
为了好玩,假设 funcA 和 funcB 在单独的 .c 文件中。当funcA 被调用时,它会改变u 的内存[以一种可预测的方式]。
两者之间没有任何改变u的布局。
然后,我们调用funcB。 byteArray 中的字节布局将是相同/可预测的数据。
这与将float 作为二进制 数据写入文件类似且工作方式相同:
#include <unistd.h>
int fd;
void
writefloat(float f)
{
float my_f = f;
write(fd,&my_f,sizeof(float));
}
void
writefloat2(float f)
{
write(fd,&f,sizeof(float));
}
void
writefloat3(float f)
{
write(fd,&f,sizeof(f));
}
如果我们使用uint32_t 而不是float,也许这会更容易看出。我们可以做一个字节序测试。 [注意:这是粗略的,不考虑像 pdp11/vax 这样的古怪字节序]:
#include <stdio.h>
#include <stdint.h>
union uint_byte {
uint32_t i;
uint8_t b[sizeof(uint32_t)];
};
union uint_byte u;
int
main(void)
{
u.i = 0x01020304;
if (u.b[0] == 0x04)
printf("cpu is little-endian\n");
else
printf("cpu is big-endian\n");
return 0;
}