输出中C结构组件的前导0xff

时间:2015-04-03 17:08:13

标签: c struct

发生了一些最奇怪的事情。遵循代码:

#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

到目前为止,您可能已经发现了输出中的瑕疵。的确,没有&#39; 0xff&#39;在内存地址0x85a013到0x85a016,每个字节也不是4个字节?!?! (我的意思是,这是否合乎逻辑?)。在我看来,这是一个显示问题,但我无法弄清楚为什么或如何。

3 个答案:

答案 0 :(得分:1)

修改

(char *)演员表更改为(unsigned char *)。并且为了防止未定义的行为,不要尝试发布标记,但使用结构大小。我的版本:

#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

答案 1 :(得分:1)

这是由签名整数提升引起的。 %x的printf说明符至少需要一个完整大小的int值。

为了避免值大小的各种问题,可变参数函数(如printf)将值转换为至少intdouble大小。

这意味着像{0x}这样的signed char值被符号扩展为32位0xffffffab

答案 2 :(得分:1)

格式说明符%xunsigned int中需要printf个参数。仅当int值为非负数时,才允许在其位置使用int参数。如果值为负,则行为未定义。

您正在为char格式说明符传递%x参数。可变参数char参数自动转换为int并传递为int。如果原始char(因此结果int)恰好具有负值,则行为未定义。这显然是你的情况。您的ff输出是使用int格式说明符无效尝试打印否定%x值的结果。

您可以通过多种不同方式修复损坏的代码。例如,您可以将printf参数显式转换为unsigned int类型

printf("%p -> %x\n", (void *) m, (unsigned) *((char *)m));

这将消除未定义的行为,但很可能会保持输出不变。现在,ff将显示为将负值模数转换为unsigned int类型的[完美定义]结果。

m = (struct s *)((char*) m + 1);

也是一个正式可疑的做法。该语言不保证struct指针可以保留char *指针的确切值。