2011-12-23 140 views
17

从内存的角度来看,将整数值转换为void*或反之亦然? 我的理解是void*是未指定长度的内存块的地址。
这似乎是比较苹果和橙子。将int转换为void *或反之亦然?

int myval = 5; 
void* ptr = (void*)myval; 
printf("%d",(int)ptr); 

我意识到我应该给出确切的上下文,在这里使用它。

int main(int argc, char* argv[]) { 
long  thread; /* Use long in case of a 64-bit system */ 
pthread_t* thread_handles; 

/* Get number of threads from command line */ 
if (argc != 2) Usage(argv[0]); 
thread_count = strtol(argv[1], NULL, 10); 
if (thread_count <= 0 || thread_count > MAX_THREADS) Usage(argv[0]); 

thread_handles = malloc (thread_count*sizeof(pthread_t)); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread); 

printf("Hello from the main thread\n"); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_join(thread_handles[thread], NULL); 

free(thread_handles); 
return 0; 
} /* main */ 

/*-------------------------------------------------------------------*/ 
void *Hello(void* rank) { 
long my_rank = (long) rank; /* Use long in case of 64-bit system */ 

printf("Hello from thread %ld of %d\n", my_rank, thread_count); 

return NULL; 
} /* Hello */ 

此代码来自Peter Pachecho的关于并行编程的书。

+0

可能的重复:http://stackoverflow.com/questions/3568069/is-it-safe-to-cast-an-int-to-void-pointer-and-back-to-int-again –

+1

它是更糟糕的是 - 至少像比较苹果和土豆 – alk

回答

17

铸造intvoid *是没有意义的,不应该这样做,因为你会试图将一个非指针指向一个指针。引用C99 standard,第6.3.2.3节第5项:

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

可能int *void *(任何指针可以转换为void *,你可以像所有指针的“基地型”想到的)。

铸造void *int是不可移植的,并且可以根据所使用的平台上的是完全错误的(例如void *也许64个位宽和int可能仅32位)。再次引用C99标准,第6.3.2.3项第6项:

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

为了解决这个问题,一些平台提供了uintptr_t,它允许您将指针作为正确宽度的数值处理。

3

这两个void*指针(或任何指针)和int大体上是数字。它们可能具有不同的比特尺寸,但指针不太可能小于int,因此使操作可逆。当然,这是非法的,你不应该取消引用没有指向有效位置的指针。

2

这很像比较苹果和橘子。此代码工作的唯一方法是因为您明确地来回转换它。

C编译器只是将来自int的字节复制到指针的空间并返回,就像如何在int中保存char一样。

如果由于某种原因,'int'比平台上的'void *'更长,这甚至可能导致您的数字被搞乱。

如果你确实想要一个数字从整数转换为指针并返回,请查看intptr_t。这种类型实际上是为了存储整数和指针而设计的。

1

能够将指针转换为指针算术的整数类型非常有用。例如,计算结构中成员的偏移量的offsetof()宏需要这种类型的转换。

但是,必须确保用于此的原始类型能够处理指针:例如,在64位Linux的情况下,当使用gcc时,情况并非如此:void *的大小为8,int为尺寸4.

offsetof宏定义为这样:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 

它是在Linux内核的双向链表实现突出使用,非常简单地定义为:

struct list_head { struct list_head *prev, *next; } 

这个结构被嵌入内核结构内,如:

struct something { 
    //... 
    struct list_head list; 
    //... 
} 

行走时的列表,代码需要抓住的指针编码结构。这是因为这(简体定义)来完成:

#define container_of(ptr, type, member) \ 
    (type *)((char *)ptr - offsetoff(type, member)) 
#define list_entry(list, type, member) \ 
    container_of(list, type, member) 

并在代码,你会经常看到:

struct something *s; 
list_for_each(listptr, somehead) { 
    s = list_entry(listptr, struct something, list); 
    //... 
} 

这就是不能没有这种宏观和指针运算的可行的。但它非常依赖工具链。

+0

这与'offsetof'有什么关系。此外,'offsetof'是平台定义的,编译器实现可以自由地以最适合的方式实现它。例如,gcc通过内在的方式来实现这一点。 –

1

这就像比较苹果和橙子,如果有任何尝试做出任何比较。但没有。基本上,在大多数体系结构中,int可以被转换为void指针而不会丢失任何信息,void指针也可以被转换回int,而不会丢失任何信息。当然,你不应该试着解引用那个指针,因为它没有指向任何有意义的内存位置:它只是一个变量,它包含与在转换之前包含的整型位模式相同的位模式。

+0

不存在信息丢失的情况。 C99标准规定:“任何指针类型都可以转换为整数类型,除前面指定的外,其结果是实现定义的,如果结果不能用整数类型表示,则行为是未定义的,结果不一定是在任何整数类型的值的范围内“。 – bobbymcr

+0

对不起,我编辑了我的答案并纠正了这个错误。 (我编辑了关于C99的一点,在'绝大多数'一词中'广'这个词)。 –

+0

“绝大多数”今天可能甚至是错误的,并且在将来会越来越少。现代架构主要是64个指针和32位'int'。 –

3

C标准规定必须有可能将一个void指针转换为一个整型,这样整型返回一个void指针将产生相同的指针。我不确定是否需要将空指针转换为整数才会产生零值,或者只有数字文字零被认为是特殊的。在很多实现中,整数值代表硬件地址,但标准没有这样的保证。在硬件地址0x12345678包含特殊陷阱的硬件上完全可能,将指针转换为整数将从硬件地址中减去0x12345678,并且将整数转换为指针会将0x12345678重新加入(因此,整数值为零代表一个空指针)。

在许多情况下,特别是在为嵌入式控制器开发时,编译器供应商将明确指定将特定整数值转换为指针类型时要访问的硬件地址。在具有单个线性地址空间的处理器上,将整数值0x12345678转换为指针通常会产生指向地址0x12345678的指针;硬件参考手册将指出该地点是否有特别的地方。在具有更多“有趣”地址空间的处理器上,可能需要使用除硬件地址之外的其他东西作为指针。例如,在古董IBM PC计算机上,显示缓冲区被映射到硬件地址0xB8000,但在大多数编译器中,地址将表示为(short far *)0xB8000000。

相关问题