【问题标题】:Convert uint_8* to any type, say "double" or a struct, in C?在 C 中将 uint_8* 转换为任何类型,比如“double”或结构?
【发布时间】:2025-12-08 21:55:01
【问题描述】:

在C语言中,我有一个类似的程序

void foo(A a);

在这里,类型 A 是已知的。它可以是任何类型、int、指针或结构,也可以是任何用户编写的类型。

现在我有一个指针指向的数据,比如 uint8_t* data 大小为 n 我如何将 data 转换为 A 类型的 a

我正在努力测试 foo,从 uint8_t* 类型和大小 n 的随机数据,使用模糊后端。

【问题讨论】:

  • "我怎样才能将数据转换为 A 类型的数据?"一般是不可能做到的。要“转换”数据,您必须知道目标类型、存储格式(目标类型对象表示)和源格式。您可以将数据“解释”为另一种类型,只需使用指针或memcpy,但这没有任何转换,并且可能会导致奇怪的结果发生,包括未定义的行为。
  • 这是有道理的。 A型实际上是已知的。我更新了帖子。谢谢!顺便说一句,“解释”是指演员表?
  • 那么A的表示是如何编码/序列化为uint8_t*的呢? IE。什么是 A,它是如何存储在 uint8_t* 中的?没有任何规则,就不可能“猜测”出规则是什么。可以举个例子,用现实生活中的uint8_t* 数据和样本A 类型? C 没有反射。 "interpret" means cast? 这意味着强制转换+取消引用,但强制转换可能是错误的,因为指针对齐方式和大小可能不同..
  • 我不明白你的最后一个问题。假设 A 是一个类型为“double x; int y”的结构,它占用 4+2 个字节。我想从数据 unit8_t 中获取 6 个字节并将它们解释为结构。
  • it takes 4+2 bytes. 不,它没有,有填充。然后取 4 个字节和 2 个字节并重构这些值。 memcpy(&t->x, arr, 4); memcpy(&t->y, arr+4, 2); 但这仍然 取决于数据如何序列化为 arr 并可能导致陷阱表示。为什么不喜欢t->x = arr[0]*256+arr[1]; t->y=arr[4]<<8+arr[5];

标签: c type-conversion fuzzing


【解决方案1】:

将 uint8_t* 转换为 C 中的任何类型?

在 C 语言中一般是不可能的。 C 语言没有reflection,没有它就不能说“任何类型”。在不知道“任何类型”对象表示的情况下,也不知道用于将该对象编码到uint8_t 对象的指针/数组中的序列化方法,一般情况下不可能自动猜测转换函数。

您可以解释uint8_t* 指向的字节集。使用指针别名将导致 strict alias violationaccess may not be aligned 并最终可能导致未定义的行为。您也可以使用memcpy(这很可能是您实际想要做的):

void foo(A a, size_t arrsize, uint8_t arr[arrsize]) {
    assert(arrsize >= sizeof(A)); // do some rudimentary safety checks
    memcpy(&a, arr, sizeof(A));
    // use a
    printf("%lf", a.some_member);
}

或使用union 进行类型双关,但这可能会导致trap representation可能导致程序perform a trap,但最终你会没事的。

将值数组转换到目标类型的唯一正确方法是实际编写反序列化/转换函数。该算法将取决于 A 类型的对象表示以及源类型的格式和编码(json?yaml?“raw”(?)字节 in big endian?little endian?MSB?LSB?等等。) .

请注意,uint8_t 表示一个恰好占用 8 个字节的数字,范围为 0 到 255。在 C 中表示“字节”使用 unsigned char 类型。特别提到unsigned char 有最小的对齐要求,sizeof 等于1,你可以alias any object with a char* pointer

【讨论】:

  • 感谢您的详细解答。您所说的“解释”到底是什么意思?我以为你的意思是“演员”,但在我阅读你的回答后显然不是这样。
  • “解释”我相信我的意思是正常的英语意思。你有 4 个字节 0x01 0x02 0x03 0x04。这可能被解释为一个等于 16909060 的小端 32 位数字,它可能被解释为一个等于 67305985 的大端 32 位数字,它可能被解释为两个 16 位大端数字 258 和 772 或者它可能被解释为等于 2.38793926059e-38 等的 IEEE-745 双精度数。Cast+dereference 是 C 语言的一部分,它是该语言中允许将相同字节解释为不同对象的一种方式。
【解决方案2】:

假设您从流中读取字节 (uint8_t) 并希望将数据传递给您的函数 foo

要遵循的步骤:

  • 您确定您阅读了数据类型 A 的序列化信息吗?
  • 您确定至少读取了 sizeof(A) 个字节吗?
  • 您确定您的类型 A 是(通常)可序列化的吗? (例如,如果 A 包含指向另一个对象的指针怎么办)

然后

foo((A) data); // <- remember: A is just a placeholder, but data is a pointer to uint8_t

【讨论】:

  • 谢谢!我怎么知道“类型 A 是否可序列化”?序列化对我来说似乎是一个很难的技术词汇。您能否通过编辑答案来详细说明?再次感谢!
  • 一切都是可序列化的,序列化意味着您将对象表示序列化为字节序列,然后可以将其写入文件或通过网络发送。我的意思是,如果您有一个字节序列,请确保您知道该序列包含 A 的序列化数据。序列化可以是微不足道的(如果数据结构仅包含原始类型)或更复杂,如果,如前所述,您的结构包含指向另一个对象的指针。您必须将该信息添加到序列中,以便您知道在另一边该做什么。
最近更新 更多