为什么不允许指针类型之间的左值转换?

Why aren't lvalue casts between pointer types allowed?

提问人:EatingTechnobladesRemainsAt3am 提问时间:5/2/2023 最后编辑:Peter MortensenEatingTechnobladesRemainsAt3am 更新时间:5/2/2023 访问量:183

问:

在 Windows PE 格式中,有一个包含标头数组的重定位部分:

typedef struct _RELOC_BLOCK_HDR
{
    UINT32 PageRVA;
    UINT32 BlockSize;
} RELOC_BLOCK_HDR, *PRELOC_BLOCK_HDR;

在每个标头之后,标头后面都有一个值数组:

typedef struct _RELOC_ENTRY
{
    UINT16 Offset : 12;
    UINT16 Type : 4;
} RELOC_ENTRY, * PRELOC_ENTRY;

假设我们有一个指向其中一个标头的指针:

PRELOC_BLOCK_HDR hdr;

我们想对它应用字节偏移量,以获得指向下一个标头的指针:

*(char**)&hdr += hdr->BlockSize

这种方式似乎有点傻。为什么我们不能使用

(char*)hdr += hdr->BlockSize

做同样的事情?我想不出在什么情况下,这种命名法会被证明是模棱两可的,或者它会对语言产生任何负面影响。

C 指针 转换

评论

3赞 Some programmer dude 5/2/2023
你的情况是什么?你为什么不能做?您可以修改为元素的单位吗?bufferbuffer += offsetoffsetbuffer
1赞 klutt 5/2/2023
我会说这样做是一种代码味道。如果不是 char 数组,那么你可以这样做bufferchar *p = buffer; *p += offset;
1赞 user694733 5/2/2023
为了给出合理的答案,您应该告诉我们如何声明和初始化。由于缓冲区大概是指针,因此用于初始化的变量是如何声明的。bufferbuffer
0赞 Lundin 5/2/2023
逐字节遍历一些“非字符类型”的通用数组/结构体的正确方法是:uint8_t* ptr = (uint8_t*)array; ptr[byte_no] = something;
4赞 tripleee 5/2/2023
这个问题目前正在 meta 上讨论

答:

2赞 Lundin 5/2/2023 #1

*(char**)&hdr += hdr->BlockSize在任何情况下都是错误的。您正在将 a 转换为 a,并且这些是不兼容的类型 - 取消引用会调用未定义的行为。所以代码是错误的,尽管它现在看起来“有效”。RELOC_BLOCK_HDR**char**char**

C 中有一条规则允许我们使用字符指针逐字节检查任何其他类型。此特殊规则将指针应用于字符类型 // (并且可能),但不“递归”应用于 。char*unsigned char*signed char*uint8_t*char**

现在至于为什么不起作用 - 实际上是类型,所以你不能使用不同的类型在该指针上进行指针算术,然后以某种方式将该指针算术的值存储为原始类型。错位在这里是一个严重的问题。(char*)hdr +=hdrRELOC_BLOCK_HDR*

至于如何处理这样的代码,最不坏的选择是使用字符类型的临时变量:

uint8_t* byte_ptr = (uint8_t*)hdr;
byte_ptr += hdr->BlockSize;
hdr = (RELOC_ENTRY*) byte_ptr;

这也是非常可疑和未定义的行为,也不能保证有效。但是你该怎么办......

许多旧的、臭气熏天的 Windows API 标头早于灵活的数组成员,并利用了“结构黑客”,这也是一种味道。这里的其他不良做法是匈牙利符号、在 typedef 后面隐藏指针、使用 for 位字段等。不可能从给定的结构中生成定义良好的 C 代码。UINT16

使用灵活数组成员正确编写的代码如下所示:

typedef struct
{
    uint32_t     PageRVA;
    RELOC_ENTRY  entry[];
} RELOC_BLOCK_HDR;

这假设与 - 相同,但如果不是,那么所有赌注在原始代码中也都关闭了。如果没有填充,那么必须发明一个不同的包装结构,32 字节大,包含 2 个对象。BlockSizesizeof(RELOC_ENTRY)RELOC_ENTRYRELOC_ENTRY