要理解void* 和int 之间地转换
首先理解int 和int* 之间的转换
一般我们将int* 理解为存放着某个地址的指针,地址所指的内容是一个int类型的值。
如果我们将int* 存放的内容通过* 取出来,输出到屏幕上如下所示:
int main(){
int t = 10;
int *p = &t;
printf("%d\n",*p);
return 0;
}
而实际上,int* 存的就是个地址而已,我们可以将地址打印出来:
int main(){
int t = 10;
int *p = &t;
long long tt = (int)p;
printf("%x\n%x\n",tt,&t);
return 0;
}
可以看到虽然编译时报warning了,但是int* 存放的地址打印出来了:50a0c86c 。
这个warning表示强制转换时类型的大小不同 在32位系统里,所有指针都是4个字节;64位系统里,所有指针都是8个字节。 而这里int*是8个字节的,转为long long也为8个字节所以不会丢失数据。 故可以忽略warning
上面已经实现了将int* 类型转为了long long int 类型,也就是将地址以数据的形式存到了long long int 中。
VICE VERSA.
将int 类型转为int* 类型,就是将int的数据存到了原本int* 存放地址的位置。
int main(){
int t = 10;
int *p = t;
printf("%lld\n",p);
return 0;
}
这里我们将int型的数据转为了int* 型,原本存放地址的指针直接存放了int 型的值,在读出来的时候可以直接输出p 而不需要*p ,如果用了*p 反而变成了去读10 这个地址的内容,造成段错误。
这里同样报了warning,报的是没有显示进行转换,如果加上显示进行转换,会报类型大小不匹配也就是上面的错误,但是因为int*占8个字节,int占4个字节,所以不会丢失数据,故这里的warning也忽略。
将int 和int* 之间的转换延申到void* 中
void* 可以理解为无类型指针,既然是无类型指针,不禁让人联想到泛型这个概念。
当确定了要使用int 和void* 转换,在实际使用中就将void* 看作int* ,保证类型大小不会丢失数据即可。
之所以我将它和泛型联系在一起,是因为void* 出现在各种库函数和系统调用函数的接口参数中,而真正在使用这些函数的时候,我们传递的参数又是各种类型的,总是被显示或隐式地转换为void* 。
比如:
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );
我们在使用的时候实际上进行了很多关于void* 的隐式转换,void* 在这些接口中更像是一个约束和规范,用来告诉我们这里可以传任意类型的指针。像上面这两个函数,我们就需要传指针或者数组就行了,因为这两个函数对指针参数的使用很明确。
应用场景
如果对指针参数的使用是 由我们自定义 的,那么传递void* 就更单纯地只是一个约束,我们不一定非得传数组或者指针类型的数据,可以像上面那样传递int值强转为void*。
比如:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
这个是创建线程的函数,第三个参数是一个回调函数,第四个参数是回调函数的参数。
这里就属于对指针参数的使用是我们自定义的,那么就意味着我们传递void*参数的时候不一定要传数组或者指针,可以像上面例子那样往地址中存放int值传递过去,在我们自定义的回调函数里面直接从指针里取值即可,比如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * tfn(void * arg){
printf("%d\n",arg);
return NULL;
}
int main(int argc, char * argv[]){
pthread_t tid;
int i=10;
int ret = pthread_create(&tid,NULL,tfn,(void*)i);
if(ret !=0) perror("pthread_create error");
pthread_exit(NULL);
}
这是我遇到的int转void*的使用场景,且这样强转主要是要注意int和指针类型的大小会不会丢失数据。
|