void* data 数据类型参数以及void *data[ ]解释

43 篇文章 15 订阅
38 篇文章 21 订阅

  • void 在英文中作为名词的解释为 “空虚、空间、空隙”,而在 C 语言中,void 被翻译为"无类型",相应的void * 为"无类型指针"。

  • void * 似乎只有"注释"和限制程序的作用,当然,这里的"注释"不是为我们人提供注释,而是为编译器提供一种所谓的注释。

void data[ ]是个什么鬼?

1、先来解决形如 type name[ ]的问题。这里有一个比较实在的推断方法

  • int buf[48],指的是一个数组有48个元素,每个元素都是int类型

  • char buf[48],指的是一个数组有48个元素,每个元素都是int类型

需要注意先表述元素个数再表述元素类型,这么表述的情况下就会容易理解数组里带指针的情况:

  • int *buf[48],指的是一个数组有48个元素,每个元素都是int类型,因此这个是包含有48个int 类型数据的数组;

  • char *buf[48],指的是一个数组有48个元素,每个元素都是char*类型,因此这个是包含有48个char *类型数据的数组;

由此我们就能知道, 对于void *data[ ] ,我们先分析[ ],再分析void *,可知,这个是包含了若干个void *类型的数组。

为什么说是若干,因为[ ]里面未指定具体的数字。我们在定义数组时有这么一种定义形式,如 int buf[ ] ={0,1,2,3,4};

如果[ ]里不指定具体的大小,则大小由实际赋值的内容决定,这种通配的形式会比较灵活。

这么来解答这个问题是不是有点耍流氓,其实还有理论基础的:在运算符的优先级中,[ ]运算符的优先级是处于第一级的,而运算符作为一元运算符时,优先级处于第二级,作为乘法运算符时,处于第四级,[ ]的优先级高于,因此在和[ ]同时出现时,我们应该先考虑[],再考虑,上述推断的方法也只是提供了一个方便记忆的方法;

2 、有关void 类型的困惑

确定了void *data[ ]是一个包含由若干个void *指针的元素的数组外,又有了新的问题,那么void *又是个什么鬼?

从多方查阅资料得知:void *有如下的特性:

  • void指针是一种没有类型的指针。

  • 任何指针都可以赋值给void指针。

  • void指针转换为其他指针时需要进行类型转换。

  • void指针由于不确定类型不能解引用。

因此,可以这么说,void *的存在就是为了通配各种参数类型的,如果在一个函数的参数列表中有个void *的参数,那么给它传入任何类型的指针参数都是正确的。

3、引发血案的罪魁祸首

那么void*也讲清了,那么参数列表中出现void *data[ ]时,到底该传入什么参数呢?

来讨论下如下的函数场景。

   void func1(void *_data0[ ]);

然后有一个int类型的数组: int data[10]={0};

如何给这个函数传参呢?我们都知道数组名data代表数组的首元素data[0]的地址,data本身是一个指针,那么我们传入的方式是下面的形式吗?

   func1(data);

如果是这么传入参数的话,那么如果有一个如下的函数原型

   void func1(void * _data0); 

这个该怎么传入数据呢?还是func1(data)吗?

如果照这么说,那么 void func1(void *data0[ ]) 和 void func1(void *data0)是等价的吗?

一时没有搞清楚,到底是不是一样的呢,于是简单的写了如下的测试代码看了下效果:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
 
char a[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
 
void function1(void *data,int length)
{
    char tmp[12] = {0};
    memcpy(tmp,data,length);
    for(int i = 0 ; i < length ; i++)
        printf("%d\r\n",tmp[i]);
    
    printf("\r\n");
}
 
void function2(void *data[],int length)
{
    char tmp[12] = {0};
    memcpy(tmp,data,length);
    
    for(int i = 0 ; i < length ; i++)
        printf("%d\r\n",tmp[i]);
    printf("\r\n");
}
 
void function3(void **data,int length)
{
    char tmp[12] = {0};
    memcpy(tmp,data,length);
    
    for(int i = 0 ; i < length ; i++)
        printf("%d\r\n",tmp[i]);
    printf("\r\n");
}
 
int main()
{
      function1(a,12);
      function2(a,12);
      function3(a,12);
}

通过gcc编译该程序,则有如下的打印:

gcc void_pointer.c -o void_pointer.c 
gcc: fatal error: input file ‘void_pointer.c’ is the same as output file
compilation terminated.
test@test-PC:~/Works/code_code/C/pointer$ gcc void_pointer.c -o void_pointer
void_pointer.c: In function ‘main’:
void_pointer.c:42:17: warning: passing argument 1 of ‘function2’ from incompatible pointer type [-Wincompatible-pointer-types]
       function2(a,12);
                 ^
void_pointer.c:18:6: note: expected ‘void **’ but argument is of type ‘char *void function2(void *data[],int length)
      ^
void_pointer.c:43:17: warning: passing argument 1 of ‘function3’ from incompatible pointer type [-Wincompatible-pointer-types]
       function3(a,12);
                 ^
void_pointer.c:29:6: note: expected ‘void **’ but argument is of type ‘char *void function3(void **data,int length)
      ^

从上面的打印可以看出,我们给 function2(void *data[],int length) 函数的 void *data[ ]相当于 void **,而我们给这个参数传递的是一个char*的数据,这个类型是不兼容的,我又特意写了个void function3(void **data,int length),从编译日志上 void *data[ ]void **data的类型是一致的,由此可见, void *data[ ]是一个指针的指针.因此给void **传入 char *是不正确的操作。

void * data 是一个无类型的指针参数,任意类型指针可以赋值给data,当data赋值给其他类型时需要先指定数据类型。

一、void 的作用

  • 1.对函数返回的限定,这种情况我们比较常见。

  • 2.对函数参数的限定,这种情况也是比较常见的。

一般我们常见的就是这两种情况:

  • 当函数不需要返回值值时,必须使用void限定,这就是我们所说的第一种情况。例如:void func(int a,char *b)
  • 当函数不允许接受参数时,必须使用void限定,这就是我们所说的第二种情况。例如:int func(void)

二、void 指针的使用规则

  1. void 指针可以指向任意类型的数据,就是说可以用任意类型的指针对 void 指针对 void 指针赋值。例如:
int *a;
void *p;
p=a;

如果要将 void 指针 p 赋给其他类型的指针,则需要强制类型转换,就本例而言:a=(int *)p。在内存的分配中我们可以见到 void 指针使用:内存分配函数 malloc 函数返回的指针就是 void * 型,用户在使用这个指针的时候,要进行强制类型转换,也就是显式说明该指针指向的内存中是存放的什么类型的数据 (int *)malloc(1024) 表示强制规定 malloc 返回的 void* 指针指向的内存中存放的是一个个的 int 型数据。

  1. 在 ANSI C 标准中,不允许对 void 指针进行一些算术运算如 p++ 或 p+=1 等,因为既然 void 是无类型,那么每次算术运算我们就不知道该操作几个字节,例如 char 型操作 sizeof(char) 字节,而 int 则要操作 sizeof(int) 字节。而在 GNU 中则允许,因为在默认情况下,GNU 认为 void * 和 char * 一样,既然是确定的,当然可以进行一些算术操作,在这里sizeof(*p)==sizeof(char)。void 几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个 void 变量,让我们试着来定义:
void a;

这行语句编译时会出错,提示"illegal use of type ‘void’"。即使 void a 的编译不会出错,它也没有任何实际意义。

众所周知,如果指针 p1 和 p2 的类型相同,那么我们可以直接在 p1 和 p2 间互相赋值;如果 p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。

float *p1;
int *p2;
p1 = p2;
//其中p1 = p2语句会编译出错,
//提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:
p1 = (float *)p2;

而 void * 则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换。

void *p1;
int *p2;
p1 = p2;

但这并不意味着,void * 也可以无需强制类型转换地赋给其它类型的指针。因为"无类型"可以包容"有类型",而"有类型"则不能包容"无类型"。

小心使用 void 指针类型:

按照 ANSI(American National Standards Institute) 标准,不能对 void 指针进行算法操作,即下列操作都是不合法的:

void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。
//例如:
int *pint;
pint++; //ANSI:正确

pint++ 的结果是使其增大 sizeof(int)。
但是 GNU 则不这么认定,它指定 void * 的算法操作与 char * 一致。因此下列语句在 GNU 编译器中皆正确:

pvoid++; //GNU:正确
pvoid += 1; //GNU:正确

pvoid++ 的执行结果是其增大了 1。

在实际的程序设计中,为迎合 ANSI 标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:

void * pvoid;
((char *)pvoid)++; //ANSI:错误;GNU:正确
(char *)pvoid += 1; //ANSI:错误;GNU:正确

GNU 和 ANSI 还有一些区别,总体而言,GNU 较 ANSI 更"开放",提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合 ANSI 标准。 如果函数的参数可以是任意类型指针,那么应声明其参数为void

注:void 指针可以任意类型的数据,可以在程序中给我们带来一些好处,函数中形为指针类型时,我们可以将其定义为 void 指针,这样函数就可以接受任意类型的指针。如:典型的如内存操作函数 memcpy 和 memset 的函数原型分别为:

void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

这样,任何类型的指针都可以传入 memcpy 和 memset 中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型(参见 C 语言实现泛型编程)。如果 memcpy 和 memset 的参数类型不是 void *,而是 char *,那才叫真的奇怪了!这样的 memcpy 和 memset 明显不是一个"纯粹的,脱离低级趣味的"函数!void 的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中"抽象基类"的概念,也很容易理解 void 数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个 void(让我们类比的称 void 为"抽象数据类型")变量。

三、代码示例

int  add(void *a,void *b)
{
	int c;
	c=*((int*)a)+*((int*)b);
	return c;
}
int _tmain(int argc, _TCHAR* argv[])
{
	int a=1,b=2;
	int sum;
	sum=add(&a,&b);
	cout<<sum<<endl;
	getchar();
	return 0;
}

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森明帮大于黑虎帮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值