“来自整数的指针/来自没有强制转换的指针的整数"问题 [英] "Pointer from integer/integer from pointer without a cast" issues

查看:26
本文介绍了“来自整数的指针/来自没有强制转换的指针的整数"问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是关于整数和指针问题之间的所有初始化/分配的常见问题解答.

<小时>

我想编写代码,将指针设置为特定的内存地址,例如 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 说):

<块引用>

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

<块引用>

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

信息性脚注:

<块引用>

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

此外,指针的地址可能大于 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 type int*"
  • MSVC (cl) says "initializing int* differs in levels of indirection from int".

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:

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.

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):

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.

Informative foot note:

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.

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>

这篇关于“来自整数的指针/来自没有强制转换的指针的整数"问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆