1、前言
指针变量占4个字节,不受所指向的变量类型影响,int型变量占4个字节,double占8个字节。比如int*p ,dounble *q ,p和q(类型都是*,也就是指针)指针变量都是占4个字节,但是,其分别指向的变量所占的字节数是不同的。
1)指针变量存储的是所指向变量的地址,这个地址值也就是一个整型常量。
2)int*转void*,可以不用强制类型转换(具体转换成抽象,模糊掉类型//void * 可以理解为各种指针的或)
例1:int *p; void*q; q =p;
例2:void *memset(void *s, int c, unsigned long n);
char str[5];
memset(str,0,sizeof(str)); //char*转void* 没有进行强制类型转换。
3)void*,转int*,得用强制类型转(抽象转具体//当然要表明是要转到哪一种指针类型)
int *p; void*q; p =(int*)q;
2、类型转换
1)正常情形的转换(指针转指针)
int* 、 double* 、 char* 和 struct * 转void*:直接转,不需要强制类型转(因为void*的是任意指针,范围广)
void * 转 int* 、double* 、char* 或struct * :需要强制类型转,范围大转范围小(例:void *p =NULL,int* a = (int*)p;)
2)int 和void*的转换
例1:指针转int ----->指针变量中的值是个 十六进制整型
int a =1;
int *p = &a;
printf("%x\n",(int)p);//打印出p是十六位进制数
例2:int 转void *
int a= 0x1234;
void *p;
p = (void*)a;//注意:此时p指向0x1234这个内存地址,但是内存地址并不存在。
注意:此时p指向0x1234这个内存地址,但是内存地址并不存在。
void *func()
{
return (void*)0;//相当于return NULL;
}
例3:void* 转int
int a= 0x1234;
void *p;
p = (void*)a;//注意:此时p指向0x1234这个内存地址,但是内存地址并不存在。
int b = (int)p;//void* 转int
注意:用void*与int互转,是因为二者的字节数相等的,不会出现丢位的情况,void*跟double就不行了,因为double所占字节数更多。
3、void* 是一个泛型指针,一般作为函数形参和函数返回值。
3.1、作为函数形参
使用void* 时要进行强制类型转换:
myFunc(void *temp)//函数定义
{
int result = *((int*)temp);//转换回来
}
int value =2;
myFunc((void*)&value);//函数调用时
3.2、作为函数返回值
(void*)(*start_rtn)(void*)
{
return (void*)0;
}
(void*)0等价于NULL。//NULL的ascii码值为0.
4、形参为void*的优势:
1)void*表示,此处可以是任意的指针类型;(以后就这么理解)
2)普通类型的指针不需强转,可以直接赋值给void*
void* 类型的指针赋值给普通类型指针需要强制类型转换。
普通类型的指针,虽然本身所占的字节数都相同,但是跳跃度不同,
比如,int* p ; p++;
和 double *q; q++;二者的跳跃度不同。
我们以pthread_create()函数为例分析如下:
#include <pthread.h>
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);
如上所示,pthread_create函数中使用了函数指针start_rtn,以及start_rtn的形参arg;
作为pthread_create来说,只是单纯的调用函数start_rtn,并不需要知道start_rtn函数的形参类型是什么。只需要写一种函数形式就行。
int value =2;
pthread_create(&pid,NULL,myFunc,(void*)&value);
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);
{
start_rtn(arg);
}
参数为指针时,当无须传递参数时,形参置NULL。
ps:系统仅提供了pthread_create函数, 函数 (void*)(*start_rtn)(void*)和参数void *arg都是程序员自定义的,也就是说,函数参数void*和参数void *arg 中的数据类型void* 是程序员自己确定,保持一致即可。对于系统来说,不确定程序员会传递一个什么类型的值,所以,就使用void*
另外还有另一种情况,例如文章中定义了memcpy的函数原型和函数实现
void *memcpy(void *memTo, const void *memFrom, size_t size)
{
if((memTo == NULL) || (memFrom == NULL))
return NULL;
char *tempFrom = (char *)memFrom;
char *tempTo = (char *)memTo;
while(size -- > 0)
*tempTo++ = *tempFrom++ ;
return memTo;
}
函数参数也为void * ,在函数实现中,我们都转换成char* ,并通过解引用,单个字节,单个字节的取数。
这种情况下,函数内部实现是通过char* ,但是参数类型为void*,使用该函数的时候,无论是什么指针类型,都可以直接传参即可,而不用进行强制类型转换,当然,转一下也是可以的比如
char ch[128];
struct st{
int mm;
double bb;
} nn;
nn.mm = 2;
nn.bb = 2.3;
memcpy(ch,(char*)&nn,sizeof(struct st)); //(char*)&nn 是不是用char* 转应该都行。