void * 是什么?
最近遇到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 <stdio.h>
#include <string.h>
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<stdio.h>
#include<string.h>
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<stdio.h>
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
void * 是什么?的更多相关文章
- 如何理解typedef void (*pfun)(void)
		
问题: 在刚接触typedef void (*pfun)(void) 这个结构的时候,存在疑惑,为什么typedef后只有一"块"东西,而不是两"块"东西呢?那 ...
 - C#中的null与void
		
一.null: 1.明义,null是什么意思? null是指一个变量没有指向具体对象的有效引用. 这句话什么意思呢?意思就是 1).能够使用null修饰的是变量: 2).主要指的是引用. 那么这就引出 ...
 - 你必须知道的指针基础-7.void指针与函数指针
		
一.不能动的“地址”—void指针 1.1 void指针初探 void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据.和用int表示指针异曲同工,只是更明确是“指针” ...
 - js中 javascript:void(0) 用法详解
		
点击链接不做任何事情: <a href="#" onclick="return false">test</a> <a href=& ...
 - html 空链接 href="#"与href="javascript:void(0)"的区别
		
#包含了一个位置信息 默认的锚是#top 也就是网页的上端 而javascript:void(0) 仅仅表示一个死链接 这就是为什么有的时候页面很长浏览链接明明是#但跳动到了页首 而javascrip ...
 - 原生JS:delete、in、typeof、instanceof、void详解
		
delete.in.typeof.instanceof.void详解 本文参考MDN做的详细整理,方便大家参考[MDN](https://developer.mozilla.org/zh-CN/doc ...
 - VS2012 Unit Test(Void, Action, Func) —— 对无返回值、使用Action或Func作为参数、多重载的方法进行单元测试
		
[提示] 1. 阅读文本前希望您具备如下知识:了解单元测试,了解Dynamic,熟悉泛型(协变与逆变)和Lambda,熟悉.NET Framework提供的 Action与Func委托.2.如果您对单 ...
 - IOS 杂笔-16 (-(void)scrollViewDidEndScrollingAnimation:方法使用注意)
		
今天在写项目的时候,遇到了一件令人抓狂的事情. 正如标题所示,被这个方法弄的团团转. -(void)scrollViewDidEndScrollingAnimation:是协议里的方法. 意味当动画结 ...
 - void main() && int main()
		
C/C++ 中从来没有定义过void main( ) .C++ 之父说过: The definition void main( ) { /* ... * / } is not and never ha ...
 - void 0作用
		
undefine 是可以被赋值的. 但是void 操作符 通过 计算 void 后面的变量名后还是会返回一个undefined ,这样就保证了你的undefined即使被定义了,采用void 表达式, ...
 
随机推荐
- django 对models中上传的文件或图片改名
			
def user_directory_path(instance,filename): print instance,filename ext = filename.split('.')[-1] pr ...
 - mysql where in 数组解决小tips
			
由于sql语法要求,不可在in后面直接连数组,若数组形式下,则需要转换成逗号隔开的字符串 <?php$arr = array(1,2,3,4,5);$arr_string= join(',', ...
 - P3175-[HAOI2015]按位或【min-max容斥,FWT】
			
正题 题目链接:https://www.luogu.com.cn/problem/P3175 题目大意 开始有一个\(n\)位二进制数\(s=0\),每次有\(p_i\)概率选取数字\(i\)让\(s ...
 - bzoj4589-Hard Nim【FWT】
			
正题 题目链接:https://darkbzoj.tk/problem/4589 题目大意 求有多少个长度为\(n\)的数列满足它们都是不大于\(m\)的质数且异或和为\(0\). 解题思路 两个初始 ...
 - CF461D-Appleman and Complicated Task【并查集】
			
正题 题目链接:https://www.luogu.com.cn/problem/CF461D 题目大意 \(n*n\)的网格需要填上\(x\)或\(o\),其中有\(k\)个格子已经固定,求有多少中 ...
 - P5934-[清华集训2012]最小生成树【最小割】
			
正题 题目链接:https://www.luogu.com.cn/problem/P5934 题目大意 给出\(n\)个点\(m\)条边的一张图,再加入一条边\((u,v,L)\)求至少删掉多少条边可 ...
 - 简单Tab切换
			
延迟Tab切换,使用css中的flex布局,原生js实现.(京东首页菜单也有此延迟功能哦!) 每天进步一丢丢~~ 1.延迟Tab切换 <!DOCTYPE html> <html la ...
 - 改头换面为哪般,最像Android的Windows——Win11升级安装体验
			
在过完了十一小长假之后,各位打工人.学僧党可期待的不仅仅是新一轮的工作,Windows11也在10月5日悄悄正式发布,正式版已经面向MSDN订阅用户开放下载. 作为微软金牌合作伙伴,本葡萄已在第一时间 ...
 - Tracking Analyst Tools(Tracking Analyst 工具)
			
Tracking Analyst 工具 # Process: 创建追踪图层 arcpy.MakeTrackingLayer_ta("", 输出图层, "NO_TIME_Z ...
 - Linux——Docker安装
			
1. 安装Docker i :环境准备:Linux要求内核3.0以上 ii:安装 #1.卸载旧版本 yum remove docker \ docker-client \ docker-client- ...