【问题标题】:Can't get the exact hexadecimal value from reading a file无法通过读取文件获得准确的十六进制值
【发布时间】:2016-05-23 07:40:07
【问题描述】:

代码 =>

#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 editor :%!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 是从哪里来的?

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

提前致谢。

【问题讨论】:

  • 为什么这个问题用 c++ 标记?
  • 您希望fread(&amp;tch,6,1,fp_in); 做什么?老实说,最简单的答案是这样做真的没有多大意义。
  • @DavidSchwartz,我想将前两个字节分配给 tch.c2(ab) 并将后四个字节分配给 tch.c4(cdef) 。
  • @shibly 那么为什么不编写代码来做到这一点呢?!创建一个六字节缓冲区,例如char buf[6];,将文件读入其中,然后将每个字节准确地放在您想要的位置,例如tch.c2=buf[0]; tch.c2&lt;&lt;=8; tch.c2|=buf[1]; 或任何您想要的。您不能随机转换指针并期望得到合理的结果,您必须编写您真正想要的代码。 (另外,你问错了问题。说明你想做什么,并寻求一个好的方法。不要以一开始就没有意义的方式寻求帮助来解决问题!)
  • @shibly 有没有可能,是的。聪明吗,不。当有一个更简单、100% 可移植的解决方案时,为什么还要编写复杂的、无缘无故的不可移植代码呢?而且,作为奖励,您实际上是在编写代码,完全按照您想要做的事情,而不是使用您精心设计的各种环境组合来做您想做的事情,然后如果这些情况发生变化,您的代码就会中断。

标签: c fread hexdump


【解决方案1】:

您应该为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)):添加填充以满足对齐约束(数据结构对齐会影响程序的性能和正确性)。

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

【讨论】:

  • 我还要提一下,OP 假设 short 是 2 字节宽,long 是 4 字节宽,这是错误的。 short 至少 2 个字节宽,long 至少 4 个字节宽。
  • @manlio,我无法理解填充的概念,您能详细说明一下吗?我怎样才能得到6364 的值?
  • @shibly 我已经添加了一些细节。
  • @manlio,我理解了padding的概念,如何获取6364的值
【解决方案2】:

前两个字节:6261

很明显,您在 little-endian CPU 架构上运行此代码。您的问题与字节在内存中的排序方式有关。

Here's 解释。

【讨论】:

    【解决方案3】:

    大多数编译器都支持“pack”编译指示,它允许您指定结构成员在内存中的布局方式。此示例显示使用 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
    

    【讨论】:

      【解决方案4】:

      您好,我建议您在分配之前清除您的 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
      

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

      【讨论】:

        【解决方案5】:

        如下更改 TAKECH 结构:

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

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

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

        【讨论】:

          【解决方案6】:

          如果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) 是六。
          它不是;它已被填充以使c4 的地址成为sizeof(unsigned long) 的倍数。
          当您只读取六个字节时,这会导致 tch 的一部分(tch.c4 的“顶部”两个字节)未初始化。

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

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

          然后调整字节序。

          总结:

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

          【讨论】:

          • 那么我怎样才能得到6364 的值呢?完全丢失了吗?
          猜你喜欢
          • 2020-05-01
          • 2015-01-26
          • 2021-12-26
          • 2014-02-25
          • 2013-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多