无法从读取文件中获取准确的十六进制值

Can't get the exact hexadecimal value from reading a file

代码=>

#include<stdio.h>

typedef struct {
    unsigned short c2;
    unsigned long c4;
} TAKECH;

int main() {
    TAKECH tch;
    FILE *fp_in;

    fp_in = fopen("in.txt","rb");

    fread(&tch,6,1,fp_in);

    printf("First two bytes: %x\n",tch.c2);
    printf("Next four bytes: %x\n",tch.c4);

    fclose(fp_in);

    return 0;
}

输出=>

First two bytes: 6261
Next four bytes: bfd56665

in.txt =>

abcdef

Hexeditor(vim 编辑器 :%!xxd) 显示这个 =>

0000000: 6162 6364 6566 0a                        abcdef.

需要输出说明:

First two bytes: 6261 <-- 为什么是倒序的?

First two bytes: 6162 <-- 这不是应该的吗?

为什么我无法在输出中得到 6364?我怎样才能用 printf("Next four bytes: %x\n",tch.c4); 得到接下来的四个字节(6364 6566) 为什么我得到 Next four bytes: bfd56665bfd5 来自哪里?

任何答案将不胜感激。

提前致谢。

你好,我建议你在赋值之前清除你的 tch 结构,因为它充满了垃圾。

是的,我真的不明白为什么我在这里投了反对票,但你知道我会添加一些代码来证明我的观点:

mmcmbp:scratch abe$ cat main.c 

#include <stdio.h>
#include <string.h>

typedef struct {
    unsigned short c2;
    unsigned long c4;
} TAKECH;

int main() {
    TAKECH tch;
    FILE *fp_in;

    memset(&tch, 0, sizeof(TAKECH));

    printf("Before\n");
    printf("First two bytes: %hu\n",tch.c2);
    printf("Next four bytes: %lu\n",tch.c4);

    fp_in = fopen("in.txt","rb");

    fread(&tch,6,1,fp_in);

    printf("After:\n");
    printf("First two bytes: %hu\n",tch.c2);
    printf("Next four bytes: %lu\n",tch.c4);

    fclose(fp_in);

    return 0;
}

编译:

mmcmbp:scratch abe$ clang main.c -o main

执行:

mmcmbp:scratch abe$ ./main

Before
First two bytes: 0
Next four bytes: 0
After:
First two bytes: 25185
Next four bytes: 0

并且根据字节的顺序,是的,字节顺序可以决定它,这就是其他人所说的。

First two bytes: 6261 <-- Why is it in reverse order?

很明显,您 运行 这段代码是在小端 CPU 架构上。您的问题与字节在内存中的排序方式有关。

Here's一个解释。

您应该为 fread 使用缓冲区(参见 http://en.cppreference.com/w/c/io/fread 上的示例),而不是 struct

由于 padding,您只能从文件中获得两个 "correct" 字节(6566)。 c4 的其他字节未初始化。

订单"issue"你可以看看:Why does fread mess with my byte order?


这是 machine/compiler-dependent,因此实际结果可能会有所不同。

typedef struct
{
  uint16_t c2;
  uint32_t c4;
} TAKECH;

sizeof(TAKECH)8(不是6=sizeof(c2) + sizeof(c4)):添加padding以满足对齐约束(数据结构对齐影响程序的性能和正确性) .

typedef struct
{
  uint16_t c2;  /* 2 bytes */
                /* 2 padding bytes */
  uint32_t c4;  /* 4 bytes */
} TAKECH;

(另见 Why isn't sizeof for a struct equal to the sum of sizeof of each member?)。

如果 TAKECH 像这样布局,您的代码将有效:

    Low address                                 High address
    |        c2       |                  c4                |
    | Byte 1 | Byte 0 | Byte 3 |  Byte 2 | Byte 1 | Byte 0 |

但实际布局是这样的:

    Low address                                              High address
    |        c2       |   Padding   |                  c4               |
    | Byte 0 | Byte 1 |      |      | Byte 0 | Byte 1 | Byte 2 | Byte 3 |

tch:    61       62      63     64      65       66    junk(d5) junk(bf)

第一个问题,排序,是由于您的计算机是 小端 - 多字节整数的最低有效字节存储在较低地址。

第二个问题是由于您假设 sizeof(TAKECH) 是六。
不是;它已被填充以使 c4 的地址成为 sizeof(unsigned long).
的倍数 这导致 tch 的一部分(tch.c4 的 "top" 两个字节)在您只读取六个字节时未初始化。

一个可靠且便携的解决方案是分别读取每个成员,

fread(&tch.c2, sizeof(tch.c2), 1, fp_in);
fread(&tch.c4, sizeof(tch.c4), 1, fp_in);

然后调整字节顺序。

总结:

  • 始终使用 sizeof 而不是依赖假设。
  • 处理二进制数据时,必须注意填充和字节顺序。

更改 TAKECH 结构如下:

typedef struct {
    unsigned short c2;
    unsigned long c4;
} __attribute__((packed)) TAKECH;

Here关于__attribute__((packed)的解释。

字节顺序最高为小端 cpu 或大端 cpu。如果您以大端 cpu 执行代码,您的意见是正确的。但是 PC 是小端 cpu。目前大多数平台使用小端模式,虽然支持大端模式。 Here 的更多详细信息。

大多数编译器支持 "pack" pragma,它允许您指定结构成员在内存中的布局方式。此示例显示使用 size-1 成员对齐打包将使您的结构与文件的布局匹配。但是,当您不需要时,您不想使用这种打包方式,因为它会降低性能,并且可能会因为未对齐的内存访问而导致其他问题。

#include <iostream>
#include <cstring>

typedef struct {
    unsigned short c2;
    unsigned long c4;
} TAKECH;

#pragma pack(push,1)
typedef struct {
    unsigned short c2;
    unsigned long c4;
} TAKECH_packed_1;
#pragma pack(pop)

const unsigned char data[] = "\x61\x62\x63\x64\x65\x66\x0a\xff\xfe\xfd\xfc";

int main() {
    TAKECH original;
    std::memcpy(&original, &data, sizeof(original));
    std::cout << std::hex;
    std::cout << "Default packing:\n";
    std::cout << "    c2: " << original.c2 << '\n';
    std::cout << "    c4: " << original.c4 << '\n';

    TAKECH_packed_1 packed;
    std::memcpy(&packed, &data, sizeof(packed));
    std::cout << "\nByte packing:\n";
    std::cout << "    c2: " << packed.c2 << '\n';
    std::cout << "    c4: " << packed.c4 << '\n';
}

这输出

Default packing:
    c2: 6261
    c4: ff0a6665

Byte packing:
    c2: 6261
    c4: 66656463