【问题标题】:Understanding pointer casting on struct type in C了解 C 中结构类型的指针转​​换
【发布时间】:2025-11-28 03:10:01
【问题描述】:

我试图理解这种情况下的指针转换。

# https://github.com/udp/json-parser/blob/master/json.c#L408

#define json_char char
typedef struct _json_object_entry
{
   json_char * name;
   unsigned int name_length;

   struct _json_value * value;

} json_object_entry;
typedef struct _json_value
{
struct
  {
     unsigned int length;

     json_object_entry * values;

     #if defined(__cplusplus) && __cplusplus >= 201103L
     decltype(values) begin () const
     {  return values;
     }
     decltype(values) end () const
     {  return values + length;
     }
     #endif

  } object;
}
(*(json_char **) &top->u.object.values) += string_length + 1;

由于我看到top->u.object.values 具有值的第一个元素的地址(类型:json_object_entry),然后我们得到值的地址,将其转换为字符,.. 从这里我迷路了。我真的不明白这样做的目的。

// 注意:这是为那些想知道这是什么的人准备的两遍解析器。

谢谢

【问题讨论】:

  • 也许它使用强制转换将值的更新限制为单个字节 (char),而不是任何值类型可以声明为 (int)...跨度>
  • 强制转换为char**,以便取消引用它(注意多余的外括号后面的第一个*)再次产生一个指针,尽管一个指向char。

标签: c json parsing


【解决方案1】:

_json_value::values 是指向(或进入)json_object_entrys 数组开头的指针。代码将其值调整几个字节,例如为了在实际数据之前跳过标头等。因为指针是类型化的,所以无需强制转换就可以仅以sizeof(_json_object_entry) 的数量更改其值,但显然偏移量可以具有任何值,具体取决于某些string_length。因此,指针的地址被取出,转换为 char 指针的地址(一个 char 指针可以以 1 为增量更改),取消引用,因此结果是指向 char 的指针与真实的u.object.values 位于同一位置,然后分配给。

应该补充一点,如果体系结构要求结构的最小对齐(可能取决于它们的第一个元素,这里是指针)并且字符串长度的值不是该值的倍数,那么这样的代码可能会在运行时中断结盟。那将使代码UB。如果保留对齐,我不确定代码是否名义上是 UB。

【讨论】:

  • 这个答案很好!
  • 非常好的答案。这是一位经验丰富的工程师预测的。但是我有另一个问题,为什么有人会做这样的隐藏数据存储?
  • 我认为代码来自这里:github.com/udp/json-parser/blob/master/json.h。它相当复杂;我已经阅读了几次,但仍然不确定我是否正确。它试图统一处理不同的数据类型并将它们存储在一个联合中。我假设有时数据可以包含字符串(我认为,解析代码会测试双引号并在找到结尾的时候相应地调整values)。我想数据可以包含各种类型的数据,(前导?)字符串就是其中之一。
  • 我希望更清晰地区分标记化和解释数据,但我也看到一个好的教科书架构可以在混合标记化时变成大量代码(例如识别文本中的字符串边界)与数据构建可以非常有效。由于作者试图构建一个“非常低占用空间的 JSON 解析器”(github.com/udp/json-parser),我认为效率是这里的驱动设计原则。
【解决方案2】:

作者在这里(被指控有罪......)

在第一遍中,values 尚未分配,因此解析器通过使用相同的字段来存储在第二遍中实际分配时所需的内存量(长度)。

if (state.first_pass)
    (*(json_char **) &top->u.object.values) += string_length + 1;

转换为json_char 是为了让我们将char 的倍数添加到长度,而不是json_object_entry 的倍数。

这有点(......好吧,不止一点......)像这样重新使用该字段的肮脏黑客,但它是为了节省向json_value添加另一个字段或使用联合( C89 联合不能是匿名的,所以它会使json_value 的结构有点奇怪)。

这里没有UB,因为此时我们实际上并没有将values用作结构数组,只是颠覆了类型系统并将其用作整数。

【讨论】:

  • 感谢詹姆斯的回答 :)
【解决方案3】:
    json_object_entry * values;
...
}

(*(json_char **) &top->u.object.values) += string_length + 1;

忘记类型正确性,可以折叠 & 和 *:

((json_char **) top->u.object.values) += string_length + 1;

top->u.object.values 确实是指向 values 数组的第一个元素的指针。它被类型转换为指向 json_char 的指针,然后是高级 string_length + 1 个字符。最终结果是 top->u.object.values 现在指向 (string_length + 1) json_chars 之前的位置。

【讨论】:

    最近更新 更多