该问题旨在作为整数和指针问题之间所有初始化/分配的常见问题解答条目。
我想编写将指针设置为特定内存地址(例如0x12345678
)的代码。但是,当使用gcc编译器编译此代码时,出现“初始化使指针从整数开始而没有强制转换”的警告/错误:
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"
使用其他流行的编译器进行的测试还会给出错误/警告消息:
int
类型的值不能用于初始化int*
类型的实体” int*
的间接级别与int
不同”。问题:以上示例是否有效?
以及后续问题:
这不会给出任何警告/错误:
int* p = 0;
为什么不呢?
答案 0 :(得分:4)
否,它不是有效的C,也从来都不是有效的C。这些示例是所谓的标准的约束违反。
该标准不允许您初始化/将指针分配给整数,也不允许将整数分配给指针。您需要使用强制转换强制使用类型转换:
int* p = (int*) 0x1234;
int i = (int)p;
如果不使用强制转换,则代码无效C,并且不允许编译器在不显示消息的情况下让代码通过。具体来说,这由简单分配(C17 6.5.16.1§1:
)的规则进行调节。6.5.16.1简单分配
约束
应满足以下条件之一:
- 左操作数具有原子,限定或不限定的算术类型,而右操作数具有 算术类型
- 左操作数具有结构或联合类型的原子,限定或非限定版本 与权利的类型兼容;
- 左操作数具有原子,合格或不合格的指针类型,并且(考虑类型) 左值转换后,左操作数将具有)两个操作数都是限定的指针 或兼容类型的非限定版本,并且左侧指向的类型具有所有 右边指出的类型的限定词;
- 左操作数具有原子,合格或不合格的指针类型,并且(考虑类型) 左值转换后,左操作数将具有)一个操作数是指向对象类型的指针, 另一个是指向void的合格或不合格版本的指针,类型指向 左边有右边所指类型的所有限定词;
- 左边的操作数是原子,合格或不合格的指针,右边的是空指针 不变;或
- 左边的操作数的类型是atomic,合格或不合格的_Bool,右边的是指针。
对于int* p = 0x12345678;
,左操作数是指针,右操作数是算术类型。
对于int i = p;
,左操作数是算术类型,右操作数是指针。
这些都不符合上面提到的任何限制。
关于int* p = 0;
起作用的原因,这是一个特例。左边的操作数是一个指针,右边的是 null指针常量。 More info about the difference between null pointers, null pointer constants and the NULL macro。
一些注意事项:
如果您将原始地址分配给指针,则指针可能需要volatile
限定,因为它指向的是硬件寄存器或EEPROM / Flash存储器之类的东西,它可以更改它的内容在运行时。
即使是强制转换,也不保证将指针转换为整数。该标准(C17 6.3.2.3§5和§6表示):
整数可以转换为任何指针类型。除先前指定外, 结果是实现定义的,可能未正确对齐,可能未指向 引用类型的实体,并且可能是陷阱表示。 68)
任何指针类型都可以转换为整数类型。除非事先指定,否则结果 是实现定义的。如果结果不能用整数类型表示,则行为是 未定义。结果不必在任何整数类型的值范围内。
说明性脚注:
68)用于将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构一致。
此外,与大多数64位系统一样,来自指针的地址可能大于int
内的地址。因此,最好使用uintptr_t
<stdint.h>