"Pointer from integer/integer from pointer without a cast" 个问题

"Pointer from integer/integer from pointer without a cast" issues

这个问题是针对所有 initialization/assignment 整数和指针问题的常见问题解答条目。


我想编写将指针设置为特定内存地址的代码,例如 0x12345678。但是当用 gcc 编译器编译这段代码时,我得到 "initialization makes pointer from integer without a cast" warnings/errors:

int* p = 0x12345678;

类似地,此代码给出 "initialization makes integer from pointer without a cast":

int* p = ...;
int i =  p;

如果我在变量声明行之外做同样的事情,消息是一样的,但说的是 "assignment" 而不是 "initialization":

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

使用其他流行的编译器进行的测试也给出了 error/warning 消息:

问题:上面的例子C有效吗?


还有一个后续问题:

这不给任何 warnings/errors:

int* p = 0;

为什么不呢?

不,它不是有效的 C,也从来不是有效的 C。这些示例是所谓的约束违规 标准。

标准不允许您 initialize/assign 一个指向整数的指针,也不允许一个整数指向一个指针。您需要使用强制转换手动强制类型转换:

int* p = (int*) 0x1234;

int i = (int)p;

如果您不使用强制转换,则代码不是有效的 C,您的编译器不允许在不显示消息的情况下让代码通过。强制转换运算符声明:C17 6.5.4/3:

Constraints
/--/
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.

6.5.16.1 是 简单赋值 的规则,允许某些指针的隐式转换,请参阅 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;有效,属于特例。左边的操作数是一个指针,右边的是一个空指针常量.


一些注意事项:

  • 如果将原始地址分配给指针,指针可能需要 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.

此外,指针的地址可能大于 int 中的地址,大多数 64 位系统都是这种情况。因此最好使用 <stdint.h>

中的 uintptr_t