了解 C 中结构类型的指针转换
Understanding pointer casting on struct type in C
我正在尝试理解这种情况下的指针转换。
# 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,..从这里我迷路了。我真的不明白这样做的目的。
// 注意:对于那些想知道这是什么的人来说,这是两遍解析器。
谢谢
_json_value::values
是指向(或进入)json_object_entry
数组开头的指针。该代码将其值调整了几个字节,例如,为了在实际数据之前跳过 header 等。因为指针是类型化的,所以可以在不强制转换的情况下仅更改其 sizeof(_json_object_entry)
数量中的值,但显然偏移量可以具有任何值,具体取决于某些 string_length
。所以指针的地址被获取,转换为一个 char 指针的地址(一个 char 指针可以以 1 为增量改变),取消引用所以结果是一个指向 char 的指针,与真正的 u.object.values
,然后赋值给.
如果架构要求结构的最小对齐(可能取决于它们的第一个元素,这里是指针)并且字符串长度可以有一个值,那么这样的代码可能会在 运行 时中断不是该对齐的倍数。这将使代码成为 UB。如果保留对齐方式,我不确定代码名义上是否是 UB。
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 确实是指向值数组第一个元素的指针。它被类型转换为指向 json_char 的指针,然后前进 string_length + 1 个字符。最终结果是 top->u.object.values 现在比以前指向 (string_length + 1) json_chars。
这里是作者(被指控有罪...)
在第一遍中,values
尚未分配,因此解析器通过使用相同的字段来存储实际分配时所需的内存量(长度)来作弊第二关
if (state.first_pass)
(*(json_char **) &top->u.object.values) += string_length + 1;
转换为 json_char
是为了让我们在长度上添加 char
的倍数,而不是 json_object_entry
.
的倍数
像那样重新使用该字段有点(...好吧,不止...)一个肮脏的 hack,但它是为了节省向 json_value
添加另一个字段或使用联合(C89 联合不能匿名,所以它会使 json_value
的结构有点奇怪)。
这里没有 UB,因为此时我们实际上并没有将 values
用作结构数组,只是颠覆了类型系统并将其用作整数。
我正在尝试理解这种情况下的指针转换。
# 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,..从这里我迷路了。我真的不明白这样做的目的。
// 注意:对于那些想知道这是什么的人来说,这是两遍解析器。
谢谢
_json_value::values
是指向(或进入)json_object_entry
数组开头的指针。该代码将其值调整了几个字节,例如,为了在实际数据之前跳过 header 等。因为指针是类型化的,所以可以在不强制转换的情况下仅更改其 sizeof(_json_object_entry)
数量中的值,但显然偏移量可以具有任何值,具体取决于某些 string_length
。所以指针的地址被获取,转换为一个 char 指针的地址(一个 char 指针可以以 1 为增量改变),取消引用所以结果是一个指向 char 的指针,与真正的 u.object.values
,然后赋值给.
如果架构要求结构的最小对齐(可能取决于它们的第一个元素,这里是指针)并且字符串长度可以有一个值,那么这样的代码可能会在 运行 时中断不是该对齐的倍数。这将使代码成为 UB。如果保留对齐方式,我不确定代码名义上是否是 UB。
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 确实是指向值数组第一个元素的指针。它被类型转换为指向 json_char 的指针,然后前进 string_length + 1 个字符。最终结果是 top->u.object.values 现在比以前指向 (string_length + 1) json_chars。
这里是作者(被指控有罪...)
在第一遍中,values
尚未分配,因此解析器通过使用相同的字段来存储实际分配时所需的内存量(长度)来作弊第二关
if (state.first_pass)
(*(json_char **) &top->u.object.values) += string_length + 1;
转换为 json_char
是为了让我们在长度上添加 char
的倍数,而不是 json_object_entry
.
像那样重新使用该字段有点(...好吧,不止...)一个肮脏的 hack,但它是为了节省向 json_value
添加另一个字段或使用联合(C89 联合不能匿名,所以它会使 json_value
的结构有点奇怪)。
这里没有 UB,因为此时我们实际上并没有将 values
用作结构数组,只是颠覆了类型系统并将其用作整数。