从内存的角度来看,将整数值转换为void*
或反之亦然?
我的理解是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 :(得分:20)
将int
强制转换为void *
是没有意义的,不应该这样做,因为您试图将非指针强制转换为指针。引用C99 standard,第6.3.2.3节第5项:
整数可以转换为任何指针类型。除了以前 指定,结果是实现定义的,可能不是 正确对齐,可能不指向引用的实体 类型,可能是陷阱表示。
你可以将int *
强制转换为void *
(任何指针都可以转换为void *
,您可以将其视为所有指针的“基本类型”
将void *
投放到int
是不可移植的,可能完全错误,具体取决于您使用的平台(例如void *
可能是64位宽,int
可能只有32位)。再次引用C99标准,第6.3.2.3节第6项:
任何指针类型都可以转换为整数类型。除了 之前指定的,结果是实现定义的。如果 结果不能用整数类型表示,行为是 未定义。结果不必在任何值的范围内 整数类型。
要解决此问题,某些平台提供uintptr_t
,允许您将指针视为适当宽度的数值。
答案 1 :(得分:3)
粗略地说,void*
指针(或任何指针)和int
都是数字。它们可能具有不同的比特,但指针不太可能小于int
,因此使操作可逆。当然,这是非法的,你永远不应该取消引用没有有效位置指针的指针。
答案 2 :(得分:3)
C标准指定必须可以将void指针转换为整数类型,以便将整数类型转换回void指针将产生相同的指针。我不确定是否要求将空指针转换为整数会产生零值,或者只有数字文字零被识别为特殊值。在许多实现中,整数值表示硬件地址,但标准没有这样的保证。完全有可能在包含硬件地址0x12345678的特殊陷阱的硬件上,将指针转换为整数将从硬件地址中减去0x12345678,并且将整数转换为指针会将0x12345678添加回(因此整数值为零表示空指针。)
在许多情况下,特别是在开发嵌入式控制器时,编译器供应商将明确指定在将特定整数值转换为指针类型时将访问的硬件地址。在具有单个线性地址空间的处理器上,将整数值0x12345678转换为指针通常会产生一个指向地址0x12345678的指针。硬件参考手册将指出该位置是否有任何特殊之处。在具有更多“有趣”地址空间的处理器上,可能需要使用除硬件地址之外的其他内容作为指针。例如,在古董IBM PC计算机上,显示缓冲区映射到硬件地址0xB8000,但在大多数编译器中,地址将表示为(short far *)0xB8000000。
答案 3 :(得分:2)
这就像比较苹果和橘子一样。此代码工作的唯一方法是因为您明确地来回转换它。
C编译器只是将int中的字节复制到指针和后面的空间中,就像在int中保存char一样。
如果出于某种原因,'int'比平台上的'void *'更长,这甚至可能导致你的号码混乱。
如果您确实希望有一个数字可以从整数转换为指针并返回,请查看intptr_t
。该类型实际上是为了存储整数和指针而设计的。
答案 4 :(得分:2)
能够将指针转换为指针算术的整数类型是很有用的。例如,计算结构中成员偏移量的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);
//...
}
如果没有这种宏和指针算法,这将是不可行的。但它非常依赖于工具链。
答案 5 :(得分:1)
如果正在进行任何比较,那就像将苹果与橙子进行比较一样。但事实并非如此。基本上,在大多数体系结构中,int可以被转换为void指针而不会丢失任何信息,并且void指针也可以被转换回int,同样不会丢失任何信息。当然,您不应该尝试取消引用该指针,因为它没有指向任何有意义的内存位置:它只是一个变量,包含与整数在转换之前包含的位模式相同的位模式。