关于C#:“来自整数的指针/来自不带强制转换的指针的整数”问题

“Pointer from integer/integer from pointer without a cast” issues

这个问题是作为整数和指针问题之间的所有初始化/赋值的FAQ条目。

我想编写将指针设置为特定内存地址(例如0x12345678)的代码。但是当使用gcc编译器编译此代码时,出现"初始化使指针从整数开始而没有强制转换"的警告/错误:

1
int* p = 0x12345678;

类似地,这段代码给出了"初始化从指针转换为整数而不进行强制转换":

1
2
int* p = ...;
int i =  p;

如果我在变量声明行之外执行相同的操作,则消息相同,但显示"赋值"而不是"初始化":

1
2
p = 0x12345678; //"assignment makes pointer from integer without a cast"
i = p;          //"assignment makes integer from pointer without a cast"

使用其他流行的编译器进行的测试还给出错误/警告消息:

  • 铛说"不兼容的整数到指针转换"
  • icc说:"不能将类型为int的值用于初始化类型为int*的实体"
  • MSVC(cl)说:"初始化int*的间接级别与int不同"。

问题:以上示例是否有效?

还有一个后续问题:

这不会给出任何警告/错误:

1
int* p = 0;

为什么不?


不,它不是有效的C,并且从来都不是有效的C。这些示例是所谓的违反标准的约束。

该标准不允许您初始化/将指针分配给整数,也不允许将整数分配给指针。您需要使用强制转换强制使用类型转换:

1
2
3
int* p = (int*) 0x1234;

int i = (int)p;

如果不使用强制转换,则代码无效C,并且不允许编译器在不显示消息的情况下让代码通过。具体来说,这受简单分配规则C17 6.5.16.1§1的约束:

6.5.16.1 Simple assignment

Constraints

One of the following shall hold:

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has
    arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type
    compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type
    the left operand would have after lvalue conversion) both operands are pointers to qualified
    or unqualified versions of compatible types, and the type pointed to by the left has all the
    qualifiers of the type pointed to by the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type
    the left operand would have after lvalue conversion) one operand is a pointer to an object type,
    and the other is a pointer to a qualified or unqualified version of void, and the type pointed to
    by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer
    constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

int* p = 0x12345678;的情况下,左操作数是指针,右操作数是算术类型。
int i = p;的情况下,左操作数是算术类型,右操作数是指针。
这些都不符合上面引用的任何约束。

至于为什么int* p = 0;起作用,这是一个特例。左边的操作数是一个指针,右边的是一个空指针常量。有关空指针,空指针常量和NULL宏之间的区别的更多信息。

注意事项:

  • 如果将原始地址分配给指针,则该指针可能需要使用volatile限定,因为它所指向的位置类似于硬件寄存器或EEPROM / Flash存储器位置,可以在运行时更改其内容。

  • 即使使用强制转换,也绝不能保证将指针转换为整数。标准(C17 6.3.2.3§5和§6表示):

    An integer may be converted to any pointer type. Except as previously specified, the
    result is implementation-defined, might not be correctly aligned, might not point to an
    entity of the referenced type, and might be a trap representation. 68)

    Any pointer type may be converted to an integer type. Except as previously specified, the result
    is implementation-defined. If the result cannot be represented in the integer type, the behavior is
    undefined. The result need not be in the range of values of any integer type.

    信息脚注:

    68) The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.

    此外,与大多数64位系统一样,指针的地址可能大于int内的地址。因此,最好使用中的uintptr_t