将整数值转换为void*并在POSIX中再次返回是否总是安全的?

Qua*_*dom 23 c posix pthreads c99

这个问题几乎与我发现的其他一些问题相同,但这个问题特别涉及POSIX,这是我在pthreads中遇到的一个非常常见的例子.我主要关注当前的事态(即C99和POSIX.1-2008或更高版本),但任何有趣的历史信息当然也很有趣.

问题基本上归结为b是否总是采用与以下代码中的a相同的值:

long int a = /* some valid value */
void *ptr = (void *)a;
long int b = (long int)ptr;
Run Code Online (Sandbox Code Playgroud)

我知道这通常有效,但问题是它是否是正确的事情(即,C99和/或POSIX标准是否保证它能够正常工作).

说到C99,它似乎没有,我们有6.3.2.3:

5整数可以转换为任何指针类型.除了之前指定的,结果是实现定义,可能没有正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示.56)

6任何指针类型都可以转换为整数类型.除了之前指定的,结果是实现定义.如果结果无法以整数类型表示,则行为未定义.结果不必在任何整数类型的值范围内.

即使使用intptr_t,标准似乎只能保证任何有效的void*都可以转换为intptr_t并再次返回,但它不能保证任何intptr_t都可以转换为void*并再次返回.

但是,POSIX标准仍然可以允许这样做.

我没有很大的愿望使用void*作为任何变量的存储空间(即使POSIX应该允许它我觉得它很难看),但我觉得我必须要问因为pthreads_create函数的常用示例使用了start_routine的参数是一个整数,它作为void*传入,并在start_routine函数中转换为int或long int.例如,此联机帮助页有这样的示例(请参阅完整代码的链接):

//Last argument casts int to void *
pthread_create(&tid[i], NULL, sleeping, (void *)SLEEP_TIME);
/* ... */
void * sleeping(void *arg){
    //Casting void * back to int
    int sleep_time = (int)arg;
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

我也在教科书(Peter S. Pacheco的并行编程简介)中看到了类似的例子.考虑到它似乎是一个常见的例子,人们应该比我更了解这些东西,我想知道我是否错了,这实际上是一个安全和便携的事情.

Kei*_*son 13

正如您所说,C99不保证任何整数类型都可以转换void*回来而不会丢失信息.它确实做出类似的担保intptr_tuintptr_t定义<stdint.h>,但这些类型是可选的.(保证void*可以转换为a {u,}intptr_t而不丢失信息;对任意整数值都没有这样的保证.)

POSIX似乎也没有做出任何此类保证.

的POSIX描述<limits.h>要求intunsigned int为至少32位.这超出了C99要求,它们至少为16位.(实际上,要求是根据范围而不是大小,但效果是,int并且unsigned int必须至少为32(在POSIX下)或16(在C99下)位,因为C99需要二进制表示.)

的POSIX描述<stdint.h>说,intptr_tuintptr_t必须至少为16位,由C标准所施加的相同要求.由于void*可以在intptr_t不丢失信息的情况下转换回来,这意味着void*可能只有16位.将其与int至少32位的POSIX要求(以及至少32位的POSIX和C要求long)相结合,并且可能void*没有足够大的值来保持intlong不丢失信息.

POSIX描述pthread_create()与此并不矛盾.它只是说arg(void*第四个参数pthread_create())传递给start_routine().据推测,意图是arg 指向start_routine()可以使用的一些数据.POSIX没有显示用法的例子arg.

你可以在这里看到POSIX标准; 你必须创建一个免费帐户来访问它.


Gre*_*ods 6

在回答的重点到目前为止似乎是一个指针的宽度,并确如@Nico指出(和@Quantumboredom也评论指出),有一个可能性,intptr_t可能比一个指针宽.@Kevin的回答暗示了另一个重要问题,但没有完全描述它.

此外,虽然我不知道标准的具体段落,哈比森和斯蒂尔指出,intptr_tuintptr_t可选类型也和可能甚至不是一个有效的C99实现存在.OpenGroup表示符合XSI标准的系统必须支持这两种类型,但这意味着普通的POSIX因此不需要它们(至少在2003版本中).

这里真正被遗漏的部分是指针不必总是具有与整数的内部表示匹配的简单数字表示.这一直是如此(自K&R 1978以来),我很确定POSIX小心不要否定这种可能性.

因此,C99并不要求它能够使用一个指针转换为intptr_t IFF这种类型的存在,然后再返回到一个指针再次使得新指针仍指向同一对象在内存中的旧指针,事实上,如果指针具有非整数表示这暗示存在可以将一组特定整数值转换为有效指针的算法.然而,这也意味着并非所有整数之间的整数INTPTR_MININTPTR_MAX必然是有效的指针值,即使intptr_t(和/或uintptr_t)的宽度与指针的宽度完全相同.

因此,标准不能保证任何intptr_tuintptr_t可以转换为一个指针和回到相同的整数值,或者甚至是一组的整数值可以生存这种转换,因为他们不可能限定用于将整数的所有可能的规则和算法值成指针值.即使对于所有已知的体系结构,这样做仍然可能妨碍标准适用于尚未发明的新型体系结构.