无法从读取文件中获取准确的十六进制值
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: bfd56665
,bfd5
来自哪里?
任何答案将不胜感激。
提前致谢。
你好,我建议你在赋值之前清除你的 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" 字节(65
和 66
)。 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
代码=>
#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: bfd56665
,bfd5
来自哪里?
任何答案将不胜感激。
提前致谢。
你好,我建议你在赋值之前清除你的 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" 字节(65
和 66
)。 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