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

时间:2016-05-23 07:40:07

标签: c fread hexdump

代码=>

#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;
}

输出=&gt;

First two bytes: 6261
Next four bytes: bfd56665

in.txt =&gt;

abcdef

Hexeditor(vim编辑器:%!xxd)显示此=&gt;

0000000: 6162 6364 6566 0a                        abcdef.

需要解释输出:

First two bytes: 6261&lt; - 为什么它的顺序相反?

First two bytes: 6162&lt; - 不应该这样吗?

为什么我不能在输出中获得6364?如何使用printf("Next four bytes: %x\n",tch.c4);获得接下来的四个字节(6364 6566)为什么我得到Next four bytes: bfd56665 bfd5来自哪里?

任何答案都将受到高度赞赏。

提前致谢。

6 个答案:

答案 0 :(得分:3)

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

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

对于订单“问题”,您可以查看:Why does fread mess with my byte order?

这取决于机器/编译器,因此实际结果可能会有所不同。

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

sizeof(TAKECH)8(不是6 = sizeof(c2) + sizeof(c4)):添加了填充以满足对齐约束(数据结构对齐会影响程序的性能和正确性)。< / p>

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?)。

答案 1 :(得分:2)

  

前两个字节:6261&lt; - 为什么它的顺序相反?

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

Here's解释。

答案 2 :(得分:1)

您可以建议您在分配之前清除您的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

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

答案 3 :(得分:1)

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

#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

答案 4 :(得分:0)

如果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)

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

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

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

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

然后调整字节顺序。

摘要:

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

答案 5 :(得分:0)

更改TAKECH结构如下:

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

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

字节顺序取决于little-endian cpu或big-endian cpu。如果您在big-endian cpu中执行代码,那么您的意见是正确的。但PC是一个小端cpu。目前最平台使用的是little-endian模式,虽然支持big-endian模式。 Here更详细。