在 C 中将字节解析为结构体

发布于 2025-01-16 13:39:19 字数 866 浏览 3 评论 0原文

我正在尝试解析这 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++以供参考。

I am trying to parse these 30 bytes:

00000000: 4353 4333 3630 4653 0200 0000 1900 0000
00000010: 0002 0000 0032 0000 0035 0000

Into a struct (also 30 bytes):

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;

I am first reading the 30 bytes into a buffer then using memcpy() to copy desired memory.

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.

I am then getting a segfault error :(

Am I misusing the memcpy() function? Sorry, I'm coming from c++ for reference.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

淤浪 2025-01-23 13:39:19
unsigned char buf[30]; //should this buffer be size_t 31?

您无法准确推断出 30 或 31 字节。在您的情况下,结构体的成员字段编译器是免费的(并且将)在结构体成员之间和/或结构体末尾应用填充字节。

鉴于 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 调用。

unsigned char buf[30]; //should this buffer be size_t 31?

You can't infer 30 or 31 bytes accurately. In your case, the member fields of the struct The compiler is free (and will) apply padding bytes between members of the struct and/or at the end of the struct.

Given that uint32_t file_system_block_count is preceeded by 10 bytes (not a number divisible by 4), you can't assume that the address of this field is exactly 10 bytes offset from the start of the struct instance. That's a very likely place for padding to exist.

You could to this:

unsigned char buf[sizeof(struct superblock_t)];
read(file_descriptor, buf, sizeof(struct superblock_t));

And that works so long as the file was written with code generated by the same compiler and compiler flags such that the same padding bytes are applied to both the reading and writing code. Another program saving that file, which was compiled differently, might have a different size for superblock. (We could also discuss difference of endianness of integers, but I digress).

A better solution is to always serialize the reading and writing of your struct on a member by member basis

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));
...

The above implies you wrote the members out to file using a similar code path that issued individual write calls for each field.

贪恋 2025-01-23 13:39:19

原型(例如在这里找到 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

这里不讨论基于假设的2。尽管我认为还有更好的方法。)

The prototype (found e.g. here https://en.cppreference.com/w/c/string/byte/memcpy ) is

void* memcpy( void *dest, const void *src, size_t count );

It wants pointers for destination.
In more than one case you provide integers, writing at runtime to whatever the compiler understands there does not surprise me by causing a segfault.
And the warnings you quote say the same.

So the answer to your question is yes.
You are misusing the memcpy function.

You need to provide pointers instead of copies.
E.g.:

memcpy(&(super_block.block_size), buf + 8, 2); // the generous () for clarity

(Not discussing the assumption-based 2 here. Though I think there are better ways.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文