【问题标题】:Does setting a void * value to a intptr_t variable require an explicit cast?将 void * 值设置为 intptr_t 变量是否需要显式转换?
【发布时间】:2017-02-23 01:36:37
【问题描述】:

当我尝试将 void * 值分配给 intptr_t 变量时,我似乎无法理解 GCC 编译器警告。具体来说,当我使用-std=c99 -pedantic 编译时,我收到以下关于第 7 行变量z 初始化的警告:

警告:初始化从没有强制转换的指针生成整数 [-Wint-conversion]

这里是源代码:

#include <stdint.h>

int main(void){
        unsigned int x = 42;
        void *y = &x;

        intptr_t z = y; /* warning: initialization makes integer from pointer without a cast [-Wint-conversion] */

        return 0;
}

当然,如果我明确地将y 转换为intptr_t,那么警告就会消失。但是,当intptr_t 的全部目的在于转换和操作void * 值时,为什么会出现隐式转换警告。

来自 C99 标准的 7.18.1.4 部分:

以下类型指定一个有符号整数类型,其属性是任何有效的 指向 void 的指针可以转换为这种类型,然后再转换回指向 void 的指针, 结果将等于原始指针:

intptr_t

我是在误解标准,还是在这种情况下 GCC 在其“integer from pointer”检查中过于迂腐?

【问题讨论】:

  • 并不过分迂腐。隐式转换只是警告存在的常见错误来源。想想当涉及重载解决方案时它会如何变得痛苦。因此,如果您真的知道自己在做什么,请通过(希望是 c++ 风格的)强制转换来通知编译器。
  • intptr_t 是一个普通的整数类型。唯一的保证是它可以保存void *的所有相关信息,其他整数类型不保证。所以警告是正确的。为什么首先要使用整数?通常,对于地址上的低级算术或位操作执行此操作。那么强烈推荐uintptr_t。否则,如果您真的想要一个整数来保存指针,请考虑三次(!)。
  • @StoryTeller:考虑到这是 C99,C++ 风格的演员表是个坏主意。
  • @user2357112:非常糟糕的建议!应该非常认真地对待 C 中的警告!
  • @user2357112: initptr_t 是标准整数类型之一的typedef,与任何其他类型一样,但 5(无符号为 6)标准整数类型除外。编译器有充分的理由不知道intptr_t 的附加含义。大多数时候,将指针分配给整数 错误(忘记取消引用指针)。如果您也将指针分配给char,编译器也会发出警告。那也是一个整数类型。你认为这个警告也可以忽略?

标签: c pointers gcc c99 gcc-warning


【解决方案1】:

总结!如有任何错误,请提前致歉——请给我留言。

在 C99 中:

  • 任何指针都可以转换为整数类型。1
  • 您可能想要这样做,例如,if you are implementing your own operating system!
  • 指针和整数之间的转换可能会出错,1通常不是你想要的。
  • 因此,当您将指针转换为整数而不进行强制转换时,编译器会发出警告。这并不过分迂腐,而是为了让您免于未定义的行为。
  • intptr_t(和uintptr_t,以及自始至终)只是一个整数类型,2,因此它与任何其他指针到整数的转换具有相同的风险。因此,您会收到相同的警告。
  • 然而,使用intptr_t,您至少知道指针的转换不会截断任何位。因此,如果您真的需要指针的整数值,那么这些就是要使用的类型——显式转换。

    • 规范1, #6 这么说

      ... 结果是实现定义的。如果结果不能用整数类型表示, 行为未定义。

      使用intptr_t,结果可以用整数类型表示。因此,行为不是未定义,而仅仅是实现定义。这就是(据我所知)为什么这些类型可以安全地用于从指针接收值。

编辑

下面的参考 1 是第 6.3 节“转换”的一部分。规范说:3

几个运算符会自动将操作数值从一种类型转换为另一种类型。这 子条款指定了这种隐式转换所需的结果...

并参考第 6.5.4 节讨论显式强制转换。因此,参考 1 中的讨论确实涵盖了从任何指针类型到 intptr_t 的隐式转换。根据我的阅读,从void *intptr_t 的隐式转换是合法的,并且具有实现定义的结果。1, 4

关于显式强制转换是否应该gcc -pedantic 认为应该,而且必须有充分的理由! :) 我个人同意明确的演员表更清楚。我也认为如果可能的话,代码应该在没有警告的情况下编译,所以如果它是我的代码,我会添加显式转换。

参考文献

1C99 draft(因为我没有最终规范的副本),秒。 6.3.2.3 #5 和 #6)。

2Id.,秒。 7.18.1.4

3Id.,秒。 6.3

4Id.,秒。 3.4.1,将“实现定义的行为”定义为“每个实现都记录了如何做出选择的未指定行为”。这意味着转换是合法的,但在一个平台上的结果可能与在另一个平台上的不同。

【讨论】:

  • 我现在从第 6.3.2.3 节的第 #5 和 #6 段中看到,整数和指针之间的转换存在固有风险(因此 intptr_tvoid *)。说从void *intptr_tvoid * 的转换不会冒未定义行为 的风险(如第#6 段所述),因为intptr_t 被定义为能够代表void * 转换的结果?
  • 澄清一下:隐式转换对void *intptr_t 有效(尽管具有实现定义的结果),但通常建议使用显式转换以明确代码的意图。对吗?
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 2015-02-26
  • 2023-04-05
  • 2012-02-13
  • 2020-03-24
  • 2011-11-15
  • 2014-11-08
相关资源
最近更新 更多