void * 是什么? 您所在的位置:网站首页 c语言是什么语言类型 void * 是什么?

void * 是什么?

2024-02-12 04:10| 来源: 网络整理| 查看: 265

最近遇到void *的问题无法解决,发现再也无法逃避了(以前都是采取悄悄绕过原则),于是我决定直面它。

在哪遇到了? 这里写图片描述

线程创建函数pthread_create()的最后一个参数void *arg,嗯?传地址还是传值?传值好像有警告。

还有别的出现的地方呢

这里写图片描述

看memcpy(),返回值和参数都有void *,那又怎么传呢?下面我们首先来说说void *是什么。

一:void *是什么?

C语言中,*类型就是指针类型。比如 int *p,double *q,虽然是不一样的指针,但是大小却一样sizeof(p) == sizeof(q),其实很容易理解,因为他们都是同一种类型*类型的。C语言是强类型的语言。对类型的区分十分严格。那这两个有什么不同点吗?有,+1就不同了,看下面的图: 这里写图片描述

也就是对于一个指针而言,如果我们在前面规定了它的类型。那就相当于决定了它的“跳跃力”。“跳跃力”就比如说上面图中int跳了4个字节,但是double跳了8个字节。基于这样的理解,我要对void *下定义了:

void * 是一个跳跃力未定的指针

二:跳跃力什么时候定?

这就是它的神奇之处了,我们可以自己控制在需要的时候将它实现为需要的类型。这样的好处是:编程时候节约代码,实现泛型编程。比如我们经常写的排序算法,就可以这么写:

#include #include static void Swap(char *vp1, char *vp2, int width) { char tmp; if ( vp1 != vp2 ) { while ( width-- ) { tmp = *vp1; *vp1++ = *vp2; *vp2++ = tmp; } } } void BubbleSort(void *base, int n, int elem_size, int (*compare)( void *, void * )) { int i, last, end = n - 1; char *elem_addr1, *elem_addr2; while (end > 0) { last = 0; for (i = 0; i < end; i++) { elem_addr1 = (char *)base + i * elem_size; elem_addr2 = (char *)base + (i + 1) * elem_size; if (compare( elem_addr1, elem_addr2 ) > 0) { Swap(elem_addr1, elem_addr2, elem_size); last = i; } } end = last; } } int compare_int(void *elem1, void *elem2) { return (*(int *)elem1 - *(int *)elem2); } int compare_double(void *elem1, void *elem2) { return (*(double *)elem1 > *(double *)elem2) ? 1 : 0; } int main(int argc, char *argv[]) { int num_int[8] = {8,7,6,5,4,3,2,1}; double num_double[8] = {8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1}; int i; BubbleSort(num_int, 8, sizeof(int), compare_int); for (i = 0; i < 8; i++) { printf("%d ", num_int[i]); } printf("\n"); BubbleSort(num_double, 8, sizeof(double), compare_double); for (i = 0; i < 8; i++) { printf("%.1f ", num_double[i]); } printf("\n"); return 0; }

这里写图片描述

上面的compare_int和compare_double就是定它跳跃力的时候。

三:再来说memcpy

我们先来看下面这段代 码:

#include #include struct stu{ int id; int num; }; #define LEN sizeof(struct stu) /*LEN 为stu的大小*/ int main(int argc,char *argv[]) { struct stu stu1,stu2; stu1.id = 2; stu1.num = 3; char str[LEN]; memcpy(str,&stu1,LEN); /*将stu1保存进str*/ memcpy(&stu2,str,LEN); /*将str转换成stu2*/ printf("%d %d\n",stu2.id,stu2.num); /*访问stu2仍然得到 2 和 3*/ return 0; }

这里写图片描述

说明:str 是一个char *类型的,但是&stu是一个struct stu *类型的,就像我们前面说的那样,他们的“跳跃力”是不一样的,但是memcpy之所以能将它们都接受,就是因为它的参数是(void *)类型的。在参数传递的时候包容万象,全都接受,这才能体现人家是memcpy()吗,mem是内存,肯定可以不非要按照某种具体类型处理,具体至于还想memcpy的内部怎么处理,看下面:

http://blog.csdn.net/yangbodong22011/article/details/53227560

四:总结

void *是一种指针类型,常用在函数参数、函数返回值中需要兼容不同指针类型的地方。我们可以将别的类型的指针无需强制类型转换的赋值给void *类型。也可以将void *强制类型转换成任何别的指针类型,至于强转的类型是否合理,就需要我们程序员自己控制了。

#include int main(int argc,char *argv[]) { int a = 2; double b = 2.0; void *c; //定义void * int *p = &a; c = p; //将int * 转成void *, double *q = (double *)c; //将void *转成double * printf("%.f\n",*q); return 0; }

结果是不是正确呢?自己试一试吧~

------ 20210827 更新 关于上述例子,其实我本身想表达指针类型确定它解释的数据范围这个观点,也就是int *解释的范围是4个字节,但是double *解释的范围是8个字节,所以上述例子中,int * 转了 void * 再到 double *,从而将它解释的范围扩大到了8个字节,那应该输出什么结果呢?答案是从 a 的地址开始,下面 8 个字节组成的 double 的数值,a的地址解释为int是2,但是再加上4个字节,这4个字节的内存值是不确定,脏数据,因此最后的输出也是脏数据。

下面是我机器的执行结果(p.s. 我同时输出了三个变量的地址)

printf("a:%p b:%p q:%p\n", &a, &b, q);

在这里插入图片描述

参考链接 : www.0xffffff.org



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有