输出中 C 结构组件中的前导 0xff
Leading 0xff in C structure components in the output
最奇怪的事情正在发生。遵循代码:
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
struct s
{
char a;
char b;
char c;
unsigned int d;
unsigned int e;
}__attribute__((packed));
int main(void)
{
struct s *m = (void *)malloc(sizeof (struct s));
assert(m);
m->a=0x33;
m->b=0x33;
m->c=0x33;
m->d=0xabcdefab;
m->e=0x12345678;
*(char *)(m + 1) = 0;
while (*((char*)m))
{
printf("%p -> %x\n", m, *((char *)m));
m = (struct s *)((char*) m + 1);
}
}
这是输出:
0x85a010 -> 33
0x85a011 -> 33
0x85a012 -> 33
0x85a013 -> ffffffab
0x85a014 -> ffffffef
0x85a015 -> ffffffcd
0x85a016 -> ffffffab
0x85a017 -> 78
0x85a018 -> 56
0x85a019 -> 34
0x85a01a -> 12
到目前为止,您可能已经发现了输出中的奇怪之处。确实,内存中地址0x85a013到0x85a016处没有'0xff',也不是每个字节4个字节?!?! (我的意思是,这合乎逻辑吗?)。在我看来,这是一个显示问题,但我无法弄清楚为什么或如何。
编辑
将 (char *)
转换为 (unsigned char *)
。并且为了防止未定义的行为,不要尝试 post 哨兵,而是使用结构大小。我的版本:
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#pragma pack(push, 1)
struct s
{
char a;
char b;
char c;
unsigned int d;
unsigned int e;
};
#pragma pack(pop)
int main(void)
{
int i;
unsigned char *cptr;
struct s *m = malloc(sizeof (struct s));
assert(m);
m->a=0x33;
m->b=0x33;
m->c=0x33;
m->d=0xabcdefab;
m->e=0x12345678;
cptr = (unsigned char*)m;
for (i=0; i<sizeof(struct s); i++)
printf("%p -> %02x\n", (void*)(cptr+i), cptr[i]);
}
程序输出:
001C2DD0 -> 33
001C2DD1 -> 33
001C2DD2 -> 33
001C2DD3 -> ab
001C2DD4 -> ef
001C2DD5 -> cd
001C2DD6 -> ab
001C2DD7 -> 78
001C2DD8 -> 56
001C2DD9 -> 34
001C2DDA -> 12
这是由有符号整数提升引起的。 %x 的 printf
说明符至少需要一个完整大小的 int
值。
为了避免值大小的各种问题,printf
等可变参数函数将值至少转换为 int
和 double
大小。
这意味着像 0xab 这样的 signed char
值被符号扩展为 32 位 0xffffffab
格式说明符 %x
需要 printf
中的 unsigned int
个参数。仅当 int
值为非负值时才允许在其位置使用 int
参数。如果值为负,则行为未定义。
您正在为 %x
格式说明符传递 char
参数。可变参数 char
会自动转换为 int
并作为 int
传递。如果原始 char
(以及结果 int
)恰好具有负值,则行为未定义。这显然是你的情况。您的 ff
输出是您尝试使用 %x
格式说明符打印负 int
值的无效尝试的结果。
您可以通过多种不同的方式修复损坏的代码。例如,您可以将 printf
参数转换为 unsigned int
显式类型
printf("%p -> %x\n", (void *) m, (unsigned) *((char *)m));
这将消除未定义的行为,但很可能会使输出保持不变。现在 ff
将显示为负值模转换为 unsigned int
类型的 [完全定义] 结果。
这个
m = (struct s *)((char*) m + 1);
也是一种形式上有问题的做法。该语言不能保证 struct
指针可以保留 char *
指针的准确值。
最奇怪的事情正在发生。遵循代码:
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
struct s
{
char a;
char b;
char c;
unsigned int d;
unsigned int e;
}__attribute__((packed));
int main(void)
{
struct s *m = (void *)malloc(sizeof (struct s));
assert(m);
m->a=0x33;
m->b=0x33;
m->c=0x33;
m->d=0xabcdefab;
m->e=0x12345678;
*(char *)(m + 1) = 0;
while (*((char*)m))
{
printf("%p -> %x\n", m, *((char *)m));
m = (struct s *)((char*) m + 1);
}
}
这是输出:
0x85a010 -> 33
0x85a011 -> 33
0x85a012 -> 33
0x85a013 -> ffffffab
0x85a014 -> ffffffef
0x85a015 -> ffffffcd
0x85a016 -> ffffffab
0x85a017 -> 78
0x85a018 -> 56
0x85a019 -> 34
0x85a01a -> 12
到目前为止,您可能已经发现了输出中的奇怪之处。确实,内存中地址0x85a013到0x85a016处没有'0xff',也不是每个字节4个字节?!?! (我的意思是,这合乎逻辑吗?)。在我看来,这是一个显示问题,但我无法弄清楚为什么或如何。
编辑
将 (char *)
转换为 (unsigned char *)
。并且为了防止未定义的行为,不要尝试 post 哨兵,而是使用结构大小。我的版本:
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#pragma pack(push, 1)
struct s
{
char a;
char b;
char c;
unsigned int d;
unsigned int e;
};
#pragma pack(pop)
int main(void)
{
int i;
unsigned char *cptr;
struct s *m = malloc(sizeof (struct s));
assert(m);
m->a=0x33;
m->b=0x33;
m->c=0x33;
m->d=0xabcdefab;
m->e=0x12345678;
cptr = (unsigned char*)m;
for (i=0; i<sizeof(struct s); i++)
printf("%p -> %02x\n", (void*)(cptr+i), cptr[i]);
}
程序输出:
001C2DD0 -> 33
001C2DD1 -> 33
001C2DD2 -> 33
001C2DD3 -> ab
001C2DD4 -> ef
001C2DD5 -> cd
001C2DD6 -> ab
001C2DD7 -> 78
001C2DD8 -> 56
001C2DD9 -> 34
001C2DDA -> 12
这是由有符号整数提升引起的。 %x 的 printf
说明符至少需要一个完整大小的 int
值。
为了避免值大小的各种问题,printf
等可变参数函数将值至少转换为 int
和 double
大小。
这意味着像 0xab 这样的 signed char
值被符号扩展为 32 位 0xffffffab
格式说明符 %x
需要 printf
中的 unsigned int
个参数。仅当 int
值为非负值时才允许在其位置使用 int
参数。如果值为负,则行为未定义。
您正在为 %x
格式说明符传递 char
参数。可变参数 char
会自动转换为 int
并作为 int
传递。如果原始 char
(以及结果 int
)恰好具有负值,则行为未定义。这显然是你的情况。您的 ff
输出是您尝试使用 %x
格式说明符打印负 int
值的无效尝试的结果。
您可以通过多种不同的方式修复损坏的代码。例如,您可以将 printf
参数转换为 unsigned int
显式类型
printf("%p -> %x\n", (void *) m, (unsigned) *((char *)m));
这将消除未定义的行为,但很可能会使输出保持不变。现在 ff
将显示为负值模转换为 unsigned int
类型的 [完全定义] 结果。
这个
m = (struct s *)((char*) m + 1);
也是一种形式上有问题的做法。该语言不能保证 struct
指针可以保留 char *
指针的准确值。