【问题标题】:c89: Convert an int to void* and backc89:将 int 转换为 void* 并返回
【发布时间】:2011-10-25 22:08:05
【问题描述】:

首先,这不是欺骗:

Is it safe to cast an int to void pointer and back to int again?

问题的不同之处在于:我只使用 void* 来存储 int,但我从未真正将它用作 void*。

所以问题真的归结为:

是否保证 void * 至少与 int 一样宽

我无法使用 intptr_t,因为我使用的是 c89 / ANSI C。

编辑

在 C99(gcc 版本)的 stdint.h 中,我看到以下内容:

/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int        intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned long int   uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int         intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned int        uintptr_t;
#endif

我是否可以直接安装类似的东西并期望它能够工作?看起来强制转换应该起作用,因为所有 intptr_t 都是整数类型的 typedef...

【问题讨论】:

  • 为什么需要将int 存储在void * 中?
  • @Oli Charlesworth:我想构建一个灵活的数组“类”,它可以处理指向对象的指针和原始内部类型(如 int)。我曾想过尝试使用联合来强制提供足够的存储空间,但问题是铸造可能会以意想不到的方式搞砸。我知道我可以在现代 x86 上摆脱它,但我希望它是可移植的。
  • 什么选角?你在说union { int i; void *p; }吗?
  • @Oli Charlesworth:在内部我可能有一个联合,但接口类似于 int appendData( void * data );
  • 为什么不存储您的数据并使用void* 指向它?在void*内部存储一些东西几乎没有意义。

标签: c c89


【解决方案1】:

不,这不能保证是安全的。

C99 标准有这样的说法(第 6.3.2.3 节):

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

任何指针类型都可以转换为整数类型。除先前规定外, 结果是实现定义的。如果结果不能用整数类型表示, 行为未定义。结果不必在任何整数的值范围内 输入。

我非常有信心 C99 之前的版本不会有任何不同。

【讨论】:

  • 我很好奇“除了先前指定的”部分。我试图寻找相关的东西,但在几分钟内找不到......
  • @Robert:整数常量 0 转换为空指针常量就在前面,这可能是你的盲点。
【解决方案2】:

有一个C FAQ: Can I temporarily stuff an integer into a pointer, or vice versa? 。

最干净的答案是:不,这不安全,避免它并继续它。但是POSIX 要求这是可能的。所以它在 POSIX 兼容的系统上是安全的。

【讨论】:

  • POSIX 在哪里需要这个?
  • 我找不到该段落。有人告诉我,所以我可能错了。
  • 没关系,我确定它是正确的。我只是对这在实践中出现的地方感兴趣。
  • @Oli Charlesworth 我见过有信誉的文本(包括 TLPI)将 void * 中的整数作为参数传递给 pthread 函数。
  • 我不认为 POSIX 明确要求它工作,但 POSIX 需要(mmap 等)一个内存模型,因此编译器必须是恶意的才能破坏所需的转换语义。
【解决方案3】:

这是一个便携式替代品。

static const char dummy[MAX_VALUE_NEEDED];
void *p = (void *)(dummy+i); /* cast to remove the const qualifier */
int i = p-dummy;

当然,如果您需要大值,它会浪费大量的虚拟地址空间,但如果您只想传递小整数,这是一种 100% 可移植且干净的方式来将整数值存储在 void * 中。

【讨论】:

  • 我想它还会大大增加您的可执行文件大小。
  • 默认初始化为零,因此只有损坏的编译器会将其存储在可执行文件中;如果有这样的事情,一个合适的人会使用 bss 或更好但只读的 bss。
【解决方案4】:

FreeRTOS 将 Timer_t 中的计时器 ID 存储为 void* pvTimerID。因此,当使用 this 作为存储空间而不是指向某个东西的指针时,有必要将其转换为可以用作数组索引的东西。

所以要读取 id,存储为 void*:

void* pvId = pxTimer->pvTimerID; int index = (int)(pvId - NULL);

【讨论】:

    猜你喜欢
    • 2016-11-20
    • 2018-04-11
    • 2018-01-21
    • 2015-12-03
    • 1970-01-01
    • 1970-01-01
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多