C - 直接使用地址访问内存?
C - access memory directly using address?
我已经用一些书简单地学习了几个星期的 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
中,第二个 8 位存储在 0x1efb41
中,接下来的 8 位存储在0x1efb42
和 0x1efb43
中的最后 8 位。我说的对吗?
如果我是对的,有什么方法可以直接访问具有十六进制地址值0x1efb41
的第二个8位?因此我可以将数据更改为 11111111
?
数据类型中的字节顺序称为 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 *
,因为后者违反了严格的别名。
您编写的代码实际上不是有效的 C。C 有一个名为 "strict aliasing," 的规则,该规则规定如果内存区域包含一种类型的值(即 float
),它不能就像它是另一种类型一样被访问(即 int
)。这条规则起源于一些让编译器生成更快代码的性能优化。我不能说这是一个明显的规则,但这是规则。
您可以使用 union
解决此问题。如果你创建一个像 union { float num, int numAsInt }
这样的联合,你可以存储一个浮点数,然后将它作为一个整数读取。结果是未指定。或者,始终允许您以字符形式访问值的字节(只是不能更大)。 char
被给予特殊处理(大概是为了让您可以将数据缓冲区复制为字节,然后将其转换为您的数据类型并访问它,这在网络堆栈等低级代码中经常发生).
欢迎来到学习 C 的有趣角落。有 未指定 行为和 未定义 行为。非正式地,未指定的行为表示 "we won't say what happens, but it will be reasonable." C 规范不会说明字节的顺序。但它会说您将获得一些字节。未定义的行为更糟糕。未定义的行为表明 任何事情 都可能发生,从编译器错误到运行时的异常,再到完全没有(让你认为你的代码有效,但实际上却不是)。
至于值,dbush 在他的回答中指出字节的顺序是由您所在的平台定义的。您看到的是 IEE754 浮点数的 "little endian" 表示。在其他平台上,可能会有所不同。
联合双关更安全:
#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
我已经用一些书简单地学习了几个星期的 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
中,第二个 8 位存储在0x1efb41
中,接下来的 8 位存储在0x1efb42
和0x1efb43
中的最后 8 位。我说的对吗?如果我是对的,有什么方法可以直接访问具有十六进制地址值
0x1efb41
的第二个8位?因此我可以将数据更改为11111111
?
数据类型中的字节顺序称为 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 *
,因为后者违反了严格的别名。
您编写的代码实际上不是有效的 C。C 有一个名为 "strict aliasing," 的规则,该规则规定如果内存区域包含一种类型的值(即 float
),它不能就像它是另一种类型一样被访问(即 int
)。这条规则起源于一些让编译器生成更快代码的性能优化。我不能说这是一个明显的规则,但这是规则。
您可以使用 union
解决此问题。如果你创建一个像 union { float num, int numAsInt }
这样的联合,你可以存储一个浮点数,然后将它作为一个整数读取。结果是未指定。或者,始终允许您以字符形式访问值的字节(只是不能更大)。 char
被给予特殊处理(大概是为了让您可以将数据缓冲区复制为字节,然后将其转换为您的数据类型并访问它,这在网络堆栈等低级代码中经常发生).
欢迎来到学习 C 的有趣角落。有 未指定 行为和 未定义 行为。非正式地,未指定的行为表示 "we won't say what happens, but it will be reasonable." C 规范不会说明字节的顺序。但它会说您将获得一些字节。未定义的行为更糟糕。未定义的行为表明 任何事情 都可能发生,从编译器错误到运行时的异常,再到完全没有(让你认为你的代码有效,但实际上却不是)。
至于值,dbush 在他的回答中指出字节的顺序是由您所在的平台定义的。您看到的是 IEE754 浮点数的 "little endian" 表示。在其他平台上,可能会有所不同。
联合双关更安全:
#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