C - 为什么在进行指针运算时转换为 uintptr_t 而不是 char*

C - Why cast to uintptr_t vs char* when doing pointer arithmetic

我正在编写一个程序,我必须在其中修改/读取目标进程内存。

到目前为止,我使用 void* 来存储地址,如果我需要更改它们(添加偏移量或一般修改),则将它们转换为 char*

我听说过 stdint.h 中定义的那种类型,但我看不出将它用于指针运算与 char* 转换有什么区别(至少对我来说这似乎对 C89 更友好)

所以我的问题是:对于指针运算,我应该使用这两种方法中的哪一种?在任何情况下我都应该考虑使用 uintptr_t 而不是 char* 吗?

编辑 1

基本上我只需要知道这是否会产生

0x00F00BAA hard coded memory adress in target
process
void* x = (void*)0x00F00BAA;
char* y = (void*)0x00F00BAA;
x = (uintptr_t)x + 0x123;
y = (char*)y + 0x123;

x == y?
x == (void*)0x00F00CCD?
y == (void*)0x00F00CCD?

在评论中,用户 R.. 指出,如果代码正在处理的地址在当前进程中无效,则以下内容可能不正确。我已经要求 OP 进行澄清。

如果您关心代码的可移植性,请不要将 uintptr_t 用于指针运算。 uintptr_t 是整数类型。对它的任何算术运算都是整数算术,不是指针算术。

如果您有一个 void* 值并且您想要为其添加一个字节偏移量,则强制转换为 char* 是正确的方法。

uintptr_t 值的算术可能与 char* 算术的工作方式相同,但绝对不能保证。 C 标准提供的唯一保证是您可以将 void* 值转换为 uintptr_t 并再次返回,结果将与原始指针值比较。

并且该标准不保证 uintptr_t 存在。如果没有足够宽的整数类型来保存转换后的指针值而不丢失信息,则实现将不会定义 uintptr_t.

我实际上曾在 uintptr_t 上的算术不一定有效的系统(Cray 向量机)上工作过。硬件有 64 位字,机器地址包含一个字的地址。类 Unix OS 需要支持 8 位字节,因此字节指针(void*char*)包含一个字地址和一个 3 位偏移量存储在其他未使用的高位 -订购 64 位字的 3 位。 Pointer/integer 转换只是复制表示。结果是向 char* 指针加 1 会导致它指向下一个字节(偏移量在软件中处理),但是转换为 uintptr_t 并加 1 会导致它指向下一个字节下一个.

底线:如果需要指针运算,请使用指针运算。这就是它的用途。

(顺便说一句,gcc 有一个扩展,允许在 void* 上进行指针运算。不要在可移植代码中使用它。它还会导致一些奇怪的副作用,例如 sizeof (void) == 1。)