在 C 中将字节解析为结构
Parse Bytes Into Struct In C
我正在尝试解析这 30 个字节:
00000000: 4353 4333 3630 4653 0200 0000 1900 0000
00000010: 0002 0000 0032 0000 0035 0000
进入结构(也是 30 字节):
struct superblock_t {
uint8_t fs_id [8];
uint16_t block_size;
uint32_t file_system_block_count;
uint32_t fat_start_block;
uint32_t fat_block_count;
uint32_t root_dir_start_block;
uint32_t root_dir_block_count;
} PACKED;
我首先将 30 个字节读入缓冲区,然后使用 memcpy()
复制所需的内存。
unsigned char buf[30]; //should this buffer be size_t 31?
read(file_descriptor, buf, 30);
struct superblock_t super_block; //initilize a super_block
memcpy(super_block.fs_id, buf, 8);
memcpy(super_block.block_size, buf + 8, 2);
memcpy(super_block.file_system_block_count, buf + 10, 4);
// and so on for additional attributes.
然后我收到段错误 :(
我是否滥用了 memcpy()
功能?不好意思,我是c++过来的,供参考。
原型(例如在这里 https://en.cppreference.com/w/c/string/byte/memcpy 找到)是
void* memcpy( void *dest, const void *src, size_t count );
它需要指向目的地的指针。
在不止一种情况下,您提供整数,在运行时写入编译器理解的任何内容,不会因导致段错误而让我感到惊讶。
你引用的警告也是如此。
所以你的问题的答案是肯定的。
您滥用了 memcpy 函数。
您需要提供指针而不是副本。
例如:
memcpy(&(super_block.block_size), buf + 8, 2); // the generous () for clarity
(这里不讨论assumption-based 2
。虽然我认为有更好的方法。)
unsigned char buf[30]; //should this buffer be size_t 31?
您无法准确推断出 30 或 31 个字节。在您的情况下,结构的成员字段编译器是免费的(并将)在结构末尾的结构 and/or 的成员之间应用填充字节。
鉴于 uint32_t file_system_block_count
前面有 10 个字节(不是可被 4 整除的数字),您不能假设此字段的地址恰好与结构实例的开头偏移 10 个字节。这是一个很可能存在填充的地方。
你可以这样做:
unsigned char buf[sizeof(struct superblock_t)];
read(file_descriptor, buf, sizeof(struct superblock_t));
只要文件是用相同的编译器和编译器标志生成的代码编写的,这样就可以将相同的填充字节应用于读取和写入代码。另一个保存该文件的程序,其编译方式不同,可能具有不同的超级块大小。 (我们也可以讨论整数字节顺序的差异,但我离题了)。
更好的解决方案是始终逐个成员序列化结构的读写
struct superblock_t block = {0};
read(file_descriptor, &block.fs_id, sizeof(block.fs_id));
read(file_descriptor, &block.block_size, sizeof(block.block_size));
read(file_descriptor, &block.file_system_block_count, sizeof(block.file_system_block_count));
...
以上意味着您使用类似的代码路径将成员写入文件,该代码路径为每个字段发出单独的 write
调用。
我正在尝试解析这 30 个字节:
00000000: 4353 4333 3630 4653 0200 0000 1900 0000
00000010: 0002 0000 0032 0000 0035 0000
进入结构(也是 30 字节):
struct superblock_t {
uint8_t fs_id [8];
uint16_t block_size;
uint32_t file_system_block_count;
uint32_t fat_start_block;
uint32_t fat_block_count;
uint32_t root_dir_start_block;
uint32_t root_dir_block_count;
} PACKED;
我首先将 30 个字节读入缓冲区,然后使用 memcpy()
复制所需的内存。
unsigned char buf[30]; //should this buffer be size_t 31?
read(file_descriptor, buf, 30);
struct superblock_t super_block; //initilize a super_block
memcpy(super_block.fs_id, buf, 8);
memcpy(super_block.block_size, buf + 8, 2);
memcpy(super_block.file_system_block_count, buf + 10, 4);
// and so on for additional attributes.
然后我收到段错误 :(
我是否滥用了 memcpy()
功能?不好意思,我是c++过来的,供参考。
原型(例如在这里 https://en.cppreference.com/w/c/string/byte/memcpy 找到)是
void* memcpy( void *dest, const void *src, size_t count );
它需要指向目的地的指针。
在不止一种情况下,您提供整数,在运行时写入编译器理解的任何内容,不会因导致段错误而让我感到惊讶。
你引用的警告也是如此。
所以你的问题的答案是肯定的。
您滥用了 memcpy 函数。
您需要提供指针而不是副本。
例如:
memcpy(&(super_block.block_size), buf + 8, 2); // the generous () for clarity
(这里不讨论assumption-based 2
。虽然我认为有更好的方法。)
unsigned char buf[30]; //should this buffer be size_t 31?
您无法准确推断出 30 或 31 个字节。在您的情况下,结构的成员字段编译器是免费的(并将)在结构末尾的结构 and/or 的成员之间应用填充字节。
鉴于 uint32_t file_system_block_count
前面有 10 个字节(不是可被 4 整除的数字),您不能假设此字段的地址恰好与结构实例的开头偏移 10 个字节。这是一个很可能存在填充的地方。
你可以这样做:
unsigned char buf[sizeof(struct superblock_t)];
read(file_descriptor, buf, sizeof(struct superblock_t));
只要文件是用相同的编译器和编译器标志生成的代码编写的,这样就可以将相同的填充字节应用于读取和写入代码。另一个保存该文件的程序,其编译方式不同,可能具有不同的超级块大小。 (我们也可以讨论整数字节顺序的差异,但我离题了)。
更好的解决方案是始终逐个成员序列化结构的读写
struct superblock_t block = {0};
read(file_descriptor, &block.fs_id, sizeof(block.fs_id));
read(file_descriptor, &block.block_size, sizeof(block.block_size));
read(file_descriptor, &block.file_system_block_count, sizeof(block.file_system_block_count));
...
以上意味着您使用类似的代码路径将成员写入文件,该代码路径为每个字段发出单独的 write
调用。