【问题标题】:casting a pointer to integer issues warning on 64bit arch在 64 位拱门上投射指向整数的指针会发出警告
【发布时间】:2011-07-26 05:27:33
【问题描述】:

我正在编写一个使用导出符号 open_exec 的 linux 内核模块

struct file *open_exec(const char *name)

它返回一个指针,我可以用IS_ERR 宏检查错误:

if (IS_ERR(file))
    return file;

在编译期间,我收到以下警告:

warning: return makes integer from pointer without a cast

这是因为我这里的函数返回一个整数。如果我尝试投射它:

return (int) file;

我在 32 位机器上没有收到警告,但在 64 位机器上却收到警告:

warning: cast from pointer to integer of different size

这是因为 int 和指针的sizeof 在 32 位机器上是一样的,但在 64 位机器上却不同。

无论是否投射,代码似乎都可以工作。我只是想摆脱警告。

我如何正确将指针转换为整数并获得我期望的值,同时不会收到编译器警告?我期望的值本质上是 linux 内核代码库的include/asm-generic/errno-base.h 中列出的整数。

由于我只是在IS_ERR() 为真的情况下将指针视为一个整数,因此我可以确定它实际上只包含一个整数值。

【问题讨论】:

  • 警告是有道理的:如果 int 不能保存指针,您的代码可能会出现问题;可能不会,但你不能确定;如果你的函数必须返回一个指针,你为什么使用 int 作为返回类型?
  • 因为 95% 的我返回错误的情况我知道错误是什么,并指定它(即;返回 -EPERM;)。对于一个可能出错的函数,它返回一个指针,我想提取错误是什么,并报告它。
  • 我无法得到它;在返回之前“提取”错误,将其映射到一个 int,然后返回该 int ......为什么不呢?或者,报告产生它的函数内部的错误;无论如何,不​​要混合使用整数和指针,即使在 32 位上也不好(你怎么能确定地址与 -EPERM 不匹配?)
  • 查看源代码,使用 long 而不是 int 可以“解决”问题而不产生其他问题?
  • @Jim 好的,对不起,我的英语有点混淆了对概率的否定:)

标签: c pointers linux-kernel compiler-warnings kernel-module


【解决方案1】:

linux/err.h 中的 PTR_ERR() 宏(也定义了 IS_ERR() 的位置)将实际上是错误代码的指针转换为适当的类型(long)。

你应该使用类似的东西:

if (IS_ERR(file))
    return PTR_ERR(file);

在源代码中搜索PTR_ERR() 的现有用法,您会发现这是一种常见模式。

您的函数可能适合返回 long 而不是 int - 但所有错误代码都应该在 int 中表示。

【讨论】:

  • 看起来可以完成这项工作,我什至不需要投射它。谢谢!
  • 是的。可悲的是,其他答案是多么的错误......从来没有想过检查 API 或意识到这一定是一种常见的模式。
  • @Jim:说实话,一开始我误解了真正被问到的问题。如果没有在非错误情况下file 指针发生的情况的上下文,很容易得出这样的结论:在正常情况下,指针作为int 返回。然后你就会从问题真正要问的问题上分心。
【解决方案2】:

您不能正确地转换指向更小尺寸类型的指针,句号。如果您确定该指针存储的内容,您可以进行一些转换。

例如,如果您知道一个指针只设置了最低 32 位,您可以直接转换它并使用一些特定于编译器的 pragma 来抑制警告。或者,如果您想对指针进行哈希处理,以便在哈希表之类的东西中使用,您可以将高 32 位与低 32 位进行异或运算。

如果不了解以后如何使用 int,则无法决定。

【讨论】:

  • 我要在这里冒个险,说我完全确定指针,在 IS_ERR() 为真的情况下,包含 errno-base 中的值之一。 h
  • 一个很好的例子:-D
  • @Corey Henderson:假设您确定int 足够宽。然后(int)(intptr_t)value 将是我认为的方法。如果警告仍然存在,请寻找如何抑制它。
  • @Corey Henderson:以下是 Visual C++ 中的抑制方法,以便您了解stackoverflow.com/q/4193476
【解决方案3】:

我不确定我是否知道您有时想从 errno-base.h 返回一个数字,有时又返回一个指针——接收函数如何区分这两者?既然如此,那么在 Linux GCC 上,

  • int 是 32 位宽,无论您使用的是 32 位还是 64 位 linux
  • 指针在 64 位架构上为 64 位宽,在 32 位宽上 32 位架构
  • long 在 32 位架构上为 32 位宽,在 64 位上为 64 位宽 架构。
  • long long 总是 64 位宽

因此,在 64 位架构上,将指针转换为 int 意味着您会将 64 位值转换为 32 位值,并且您可以肯定会从指针中丢失部分 64 位信息——这是正如您自己指出的那样,编译器警告的全部内容。

如果您想将指针转换为“匿名”,那么您的选择应该是longlong longvoid*——void* 是最便携的。

另一种选择是将其记录为偏移量,也就是说,如果您有一个很大的内存区域,您想将其“转换”为 32 位整数,然后将其转换为类似的东西;

  static struct mybigbuffer *globalbuffer;
   int cast2int(void*x)
   {
       return (int)(globalbuffer-(struct mybigbuffer*)x);
   }

然而只有在你知道你的记忆永远不会超过 globalbuf 的 2^31 条记录并且确保您的指针在边界等上对齐 - 所以除非您 100% 确定自己知道自己在做什么,否则我也不建议这样做 - 坚持使用 long 或 void* 作为安全选项。

【讨论】:

  • 我不知道内核上下文中提供的类型,但通常在 C 中有[u]intpr_t 用于指向整数转换的指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
  • 2021-04-12
  • 2013-04-08
  • 1970-01-01
相关资源
最近更新 更多