“来自整数的指针/来自不带强制转换的指针的整数”问题

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

提问人:Lundin 提问时间:9/5/2018 最后编辑:Lundin 更新时间:1/20/2022 访问量:4379

问:

此问题旨在作为整数和指针问题之间的所有初始化/赋值的常见问题解答条目。


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

int* p = 0x12345678;

类似地,此代码给出“初始化使整数从指针而不强制转换”:

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

如果我在变量声明行之外做同样的事情,消息是相同的,但说的是“赋值”而不是“初始化”:

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

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

  • Clang 说“不兼容的整数到指针转换”
  • ICC 说“Type 的值不能用于初始化 Type 的实体intint*"
  • MSVC (cl) 表示“初始化在间接级别上与 ”。int*int

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


还有一个后续问题:

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

int* p = 0;

为什么不呢?

C 指针 强制转换 初始化 赋值运算符

评论

1赞 Antti Haapala -- Слава Україні 9/5/2018
这个问题可以明确地说“我想要一个指向内存地址0x12345678的指针”,然后答案可以解决这个问题
0赞 Lundin 9/5/2018
@AnttiHaapala是的,这是一个很好的选择,特别是因为答案已经暗示了处理内存地址时的最佳实践。我在问题的顶部添加了一个新句子。

答:

16赞 Lundin 9/5/2018 #1

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

该标准不允许初始化/分配指向整数的指针,也不允许将整数指定为指针。您需要使用强制转换手动强制进行类型转换:

int* p = (int*) 0x1234;

int i = (int)p;

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

约束
/--/
除 6.5.16.1 约束允许的情况外,涉及指针的转换应通过显式强制转换的方式指定。

6.5.16.1 作为允许指针的某些隐式转换的简单赋值规则,参见 C17 6.5.16.1 §1:

6.5.16.1 简单分配

约束

以下其中一项应成立:

  • 左操作数具有原子、限定或非限定算术类型,右操作数具有 算术类型;
  • 左操作数具有结构或联合类型的原子版本、限定版本或非限定版本 与权利类型相符;
  • 左操作数具有原子指针类型、限定指针类型或非限定指针类型,并且(考虑类型 左操作数在左值转换后将具有)两个操作数都是指向限定的指针 或兼容类型的非限定版本,并且左侧指向的类型具有所有 右边指向的类型的限定符;
  • 左操作数具有原子指针类型、限定指针类型或非限定指针类型,并且(考虑类型 左操作数在左值转换后将具有)一个操作数是指向对象类型的指针, 另一个是指向 void 的限定或非限定版本的指针,类型指向 by left 包含 right 指向的类型的所有限定符;
  • 左边的操作数是原子指针、限定指针或非限定指针,右边是空指针 不断;或
  • 左操作数具有类型 atomic、qualified 或 unqualified _Bool,右操作数是指针。

如果是 ,左操作数是指针,右操作数是算术类型。
如果是 ,左操作数是算术类型,右操作数是指针。
这些都不符合上述任何限制。
int* p = 0x12345678;int i = p;

至于为什么有效,这是一个特例。左边的操作数是指针,右边的操作数是空指针常量。有关 null 指针、null 指针常量和 NULL 宏之间差异的详细信息int* p = 0;


一些注意事项:

  • 如果将原始地址分配给指针,则指针可能需要限定,因为它指向硬件寄存器或EEPROM/闪存位置之类的东西,这些位置可以在运行时更改其内容。volatile

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

整数可以转换为任何指针类型。除前面指定外, 结果是实现定义的,可能未正确对齐,可能未指向 实体,并且可能是陷阱表示形式。68)

任何指针类型都可以转换为整数类型。除先前指定外,结果 是实现定义的。如果结果不能用整数类型表示,则行为为 定义。结果不必在任何整数类型的值范围内。

内容丰富的脚注:

68) 用于将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构保持一致。

此外,指针中的地址可能大于 ,就像大多数 64 位系统一样。因此,最好使用 fromintuintptr_t<stdint.h>

评论

0赞 Antti Haapala -- Слава Україні 9/5/2018
您可以在 C11 的脚注 67) 中添加 sth: 用于将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构保持一致。 也就是说,高质量的实现不会只是从帽子;)中提取数字
1赞 Lundin 9/5/2018
@AnttiHaapala 添加了脚注。是的,前 ANSI 倾向于将所有类型视为.无论如何,它从未被标准化。更令人担忧的是,一些半现代的嵌入式系统编译器在没有任何诊断消息的情况下允许这种代码通过。IIRC,一些较旧的 PC 编译器,如 Borland 也这样做了。int
0赞 chux - Reinstate Monica 9/5/2018
那么 iff 恰好是另一个特定于实现的空指针常量会好吗(不是约束违规)?p = 0x10000000;0x10000000
0赞 Antti Haapala -- Слава Україні 9/5/2018
“值为 0 的整数常量表达式,或转换为 void * 类型的表达式,称为空指针常量。你可以从 port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
1赞 Antti Haapala -- Слава Україні 9/5/2018
好吧,它可以肯定地使用,或者或或.但不是.不是标准所说的空指针常量。0LL0ULL'\0'(5-5)4242