问题描述
这个问题是关于整数和指针问题之间的所有初始化/分配的常见问题解答.
我想编写代码,将指针设置为特定的内存地址,例如 0x12345678
.但是当用 gcc 编译器编译这段代码时,我得到初始化使指针从整数而不进行强制转换"警告/错误:
int* p = 0x12345678;
同样,这段代码给出了初始化从指针生成整数而不进行强制转换":
int* p = ...;int i = p;
如果我在变量声明行之外做同样的事情,消息是一样的,但说的是赋值"而不是初始化":
p = 0x12345678;//赋值使指针来自整数而不进行强制转换"我 = p;//赋值从指针生成整数而不进行强制转换"
使用其他流行编译器的测试也会给出错误/警告消息:
- clang 说不兼容的整数到指针转换"
- icc 说
int
类型的值不能用于初始化int*
类型的实体" - MSVC (cl) 表示初始化
int*
的间接级别与int
不同".
问题:以上示例是否有效 C?
还有一个后续问题:
这不会给出任何警告/错误:
int* p = 0;
为什么不呢?
不,它不是有效的 C 并且从来都不是有效的 C.这些例子是所谓的标准的约束冲突.
该标准不允许您初始化/分配指向整数的指针,也不允许您初始化/分配指向指针的整数.您需要使用强制转换手动强制类型转换:
int* p = (int*) 0x1234;int i = (int)p;
如果您不使用强制转换,则代码不是有效的 C,并且您的编译器不允许在不显示消息的情况下让代码通过.具体来说,这是由简单赋值规则规定的,C17 6.5.16.1 §1:
6.5.16.1 简单赋值
约束
符合下列条件之一:
- 左侧操作数具有原子、限定或非限定算术类型,右侧具有算术类型;
- 左操作数具有结构或联合类型的原子、限定或非限定版本与权限类型兼容;
- 左操作数具有原子、限定或非限定指针类型,并且(考虑类型左操作数将在左值转换后具有)两个操作数都是指向限定的指针或兼容类型的非限定版本,并且左侧指向的类型具有所有右边所指类型的限定符;
- 左操作数具有原子、限定或非限定指针类型,并且(考虑类型左操作数在左值转换后会有)一个操作数是一个指向对象类型的指针,另一个是指向void的限定或非限定版本的指针,指向的类型左边的具有右边指向的类型的所有限定符;
- 左操作数是原子的、限定的或非限定的指针,右操作数是空指针持续的;或
- 左操作数的类型是原子的、限定的或非限定的 _Bool,右操作数是一个指针.
如果int* p = 0x12345678;
,左操作数是指针,右操作数是算术类型.
在int i = p;
的情况下,左操作数是算术类型,右操作数是指针.
这些都不符合上述任何限制条件.
至于为什么 int* p = 0;
有效,这是一个特例.左操作数是一个指针,右操作数是一个空指针常量.有关空指针、空指针常量和 NULL 宏之间区别的更多信息.
注意事项:
如果您为指针分配原始地址,则该指针可能需要
volatile
限定,因为它指向诸如硬件寄存器或 EEPROM/闪存位置之类的东西,可以在运行时更改其内容.即使使用强制转换也不能保证将指针转换为整数.标准(C17 6.3.2.3 §5 和 §6 说):
整数可以转换为任何指针类型.除先前规定外,结果是实现定义的,可能未正确对齐,可能未指向引用类型的实体,并且可能是陷阱表示.
任何指针类型都可以转换为整数类型.除先前指定的外,结果是实现定义的.如果结果不能用整数类型表示,则行为是不明确的.结果不需要在任何整数类型的值范围内.
信息性脚注:
将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构保持一致.
此外,指针的地址可能大于 int
中的地址,大多数 64 位系统都是这种情况.因此最好使用
uintptr_t
This question is meant to be a FAQ entry for all initialization/assignment between integer and pointer issues.
I want to do write code where a pointer is set to a specific memory address, for example 0x12345678
. But when compiling this code with the gcc compiler, I get "initialization makes pointer from integer without a cast" warnings/errors:
int* p = 0x12345678;
Similarly, this code gives "initialization makes integer from pointer without a cast":
int* p = ...;
int i = p;
If I do the same outside the line of variable declaration, the message is the same but says "assignment" instead of "initialization":
p = 0x12345678; // "assignment makes pointer from integer without a cast"
i = p; // "assignment makes integer from pointer without a cast"
Tests with other popular compilers also give error/warning messages:
- clang says "incompatible integer to pointer conversion"
- icc says "a value of type
int
cannot be used to initialize an entity of typeint*
" - MSVC (cl) says "initializing
int*
differs in levels of indirection fromint
".
Question: Are the above examples valid C?
And a follow-up question:
This does not give any warnings/errors:
int* p = 0;
Why not?
No, it is not valid C and has never been valid C. These examples are so-called constraint violations of the standard.
The standard does not allow you to initialize/assign a pointer to an integer, nor an integer to a pointer. You need to manually force a type conversion with a cast:
int* p = (int*) 0x1234;
int i = (int)p;
If you don't use the cast, the code is not valid C and your compiler is not allowed to let the code pass without displaying a message. Specifically, this is regulated by the rules of simple assignment, C17 6.5.16.1 §1:
- the left operand has atomic, qualified, or unqualified arithmetic type, and the right hasarithmetic type;
- the left operand has an atomic, qualified, or unqualified version of a structure or union typecompatible with the type of the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the typethe left operand would have after lvalue conversion) both operands are pointers to qualifiedor unqualified versions of compatible types, and the type pointed to by the left has all thequalifiers of the type pointed to by the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the typethe 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 toby 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 pointerconstant; or
- the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.
In case of int* p = 0x12345678;
, the left operand is a pointer and the right is an arithmetic type.
In case of int i = p;
, the left operand is an arithmetic type and the right is a pointer.
Neither of these fit in with any of the constraints cited above.
As for why int* p = 0;
works, it is a special case. The left operand is a pointer and the right is a null pointer constant. More info about the difference between null pointers, null pointer constants and the NULL macro.
Some things of note:
If you assign a raw address to a pointer, the pointer likely needs to be
volatile
qualified, given that it points at something like a hardware register or an EEPROM/Flash memory location, that can change its contents in run-time.Converting a pointer to an integer is by no means guaranteed to work even with the cast. The standard (C17 6.3.2.3 §5 and §6 says):
Informative foot note:
In addition, the address from a pointer might be larger than what will fit inside an int
, as is the case for most 64 bit systems. Therefore it is better to use the uintptr_t
from <stdint.h>
这篇关于“来自整数的指针/来自没有强制转换的指针的整数"问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!