我已经有几本书对C进行了轻松的学习。
int main(void)
{
float num = 3.15;
int *ptr = (int *)# //so I can use line 8 and 10
for (int i = 0; i < 32; i++)
{
if (!(i % 8) && (i / 8))
printf(" ");
printf("%d", *ptr >> (31 - i) & 1);
}
return 0;
}
output : 01000000 01001001 10011001 10011010
单精度3.15
中的float
是01000000 01001001 10011001 10011010
。
因此,假设ptr
指向地址0x1efb40
。
以下是问题:
正如我在书中所了解的那样,num
数据的前8位存储在0x1efb40
中,0x1efb41
中的第二8位存储在{{1}中}和0x1efb42
中的后8位。我说的对吗?
如果我是对的,我有什么办法可以直接使用十六进制地址值0x1efb43
访问第二个8位?从而可以将数据更改为类似0x1efb41
的内容吗?
答案 0 :(得分:4)
数据类型中字节的顺序称为 endianness ,并且是系统特定的。首先用最低有效字节(LSB)描述的内容称为 little endian ,这是在基于x86的处理器上可以找到的内容。
对于访问表示形式的特定字节,可以使用指向unsigned char
的指针来指向相关变量以查看特定字节。例如:
float num = 3.15;
unsigned char *p = (unsigned char *)#
int i;
for (i=0; i<sizeof(num); i++) {
printf("byte %d = %02x\n", i, p[i]);
}
请注意,这仅允许通过字符指针而不是int *
访问字节,因为后者违反了严格的别名。
答案 1 :(得分:1)
您编写的代码实际上不是有效的C。C有一个称为“严格别名”的规则,该规则指出,如果内存区域包含一种类型的值(即float
),则无法访问它。好像它是另一种类型(即int
)。此规则起源于一些性能优化,这些性能优化使编译器生成更快的代码。我不能说这是一个明显的规则,但这是规则。
您可以使用union
解决此问题。如果创建像union { float num, int numAsInt }
这样的并集,则可以存储浮点数,然后将其读取为整数。结果为未指定。另外,始终允许您以char形式访问值的字节(只是没有更大的值)。 char
得到了特殊处理(大概是为了使它能够复制,以便您可以将数据缓冲区复制为字节,然后将其强制转换为数据的类型并访问它,这在诸如网络堆栈的低级代码中经常发生)。
欢迎来到学习C的有趣角落。有未指定行为和未定义行为。非正式地,未指定的行为表示“我们不会说会发生什么,但这是合理的。” C规范不会说字节的顺序。但是会说您将得到一些字节。未定义的行为更糟糕。未定义的行为表明任何事情都可能发生,从编译器错误到运行时异常,甚至什么都没有(使您认为代码在无效时是有效的)。
对于值,dbush在他的答案中指出字节的顺序由您所使用的平台定义。您会看到IEE754浮点数的“小尾数”表示。在其他平台上,可能有所不同。
答案 2 :(得分:0)
与联盟的合作更加安全:
#include <stdio.h>
typedef union
{
unsigned char uc[sizeof(double)];
float f;
double d;
}u_t;
void print(u_t u, size_t size, int endianess)
{
size_t start = 0;
int increment = 1;
if(endianess)
{
start = size - 1;
increment = -1;
}
for(size_t index = 0; index < size; index++)
{
printf("%hhx ", u.uc[start]);
start += increment;
}
printf("\n");
}
int main(void)
{
u_t u;
u.f = 3.15f;
print(u, sizeof(float),0);
print(u, sizeof(float),1);
u.d = 3.15;
print(u, sizeof(double),0);
print(u, sizeof(double),1);
return 0;
}
您可以自己进行测试:https://ideone.com/7ABZaj