void * 是什么?

void * 是什么?

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

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

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

还有别的出现的地方呢

这里写图片描述

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

一:void *是什么?

C语言中,*类型就是指针类型。比如 int *pdouble *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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

这里写图片描述

上面的compare_intcompare_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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这里写图片描述

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

http://blog.youkuaiyun.com/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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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

参考链接 : www.0xffffff.org

版权声明:原创文章转载前请私信或者评论,一天之内回复。 https://blog.youkuaiyun.com/yangbodong22011/article/details/53224856
### void指针的基本概念 在C/C++中,`void*`指针是一种特殊的指针类型,它可以存储任何数据类型的地址。与具体的指针类型(如`int*`、`float*`)不同,`void*`指针没有关联的数据类型信息,因此它不能直接用于访问指针所指向的数据内容。为了访问数据,必须将`void*`指针转换为适当的指针类型。这种特性使得`void*`指针在处理通用数据时非常有用[^3]。 ### void指针的使用场景 1. **通用数据处理**:当需要编写能够处理多种数据类型的函数时,可以使用`void*`指针作为参数。例如,标准库函数`memcpy`和`memmove`使用`void*`指针来复制或移动内存块,无论这些内存块包含的是什么类型的数据。 2. **回调函数**:在注册回调函数时,有时需要传递一个`void*`指针作为参数,以便回调函数可以访问到特定的数据结构或上下文信息。 3. **动态内存分配**:`malloc`、`calloc`等函数返回的是`void*`指针,这是因为这些函数分配的内存块尚未指定其用途。用户需要根据自己的需求,将`void*`指针转换为相应的指针类型[^3]。 4. **接口设计**:在设计库或框架的接口时,使用`void*`指针可以增加接口的灵活性,允许接口接受或返回不同类型的数据。 ### void指针的注意事项 尽管`void*`指针提供了很大的灵活性,但在使用时也需要注意一些问题: - **类型安全**:由于`void*`指针不携带类型信息,因此在转换回具体指针类型时必须确保转换的正确性,否则可能导致未定义行为。 - **解引用限制**:不能直接对`void*`指针进行解引用操作,因为编译器不知道如何解释指针指向的数据。必须先将`void*`指针转换为适当的指针类型后才能进行解引用操作[^3]。 ### 示例代码 下面是一个简单的示例,展示了如何使用`void*`指针来传递不同类型的参数: ```c #include <stdio.h> void printValue(void* value, const char* type) { if (strcmp(type, "int") == 0) { printf("Integer: %d\n", *(int*)value); } else if (strcmp(type, "double") == 0) { printf("Double: %f\n", *(double*)value); } else if (strcmp(type, "char") == 0) { printf("Character: %c\n", *(char*)value); } } int main() { int i = 42; double d = 3.14; char c = 'A'; printValue(&i, "int"); printValue(&d, "double"); printValue(&c, "char"); return 0; } ``` 在这个例子中,`printValue`函数接受一个`void*`指针和一个表示数据类型的字符串。根据类型字符串,函数将`void*`指针转换为相应的指针类型,并打印出指针指向的值。 通过上述讨论,可以看到`void*`指针在C/C++编程中的重要性和实用性,同时也需要注意其使用时的安全性和正确性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值