1、简单认识一下void
* s" s& l/ I" Y 今天跟大家介绍的知识是C语言中的void关键字的用法,void在大部分小伙伴的程序中都只是用于函数无参数传入,或者无类型返回。然而我们平时所定义的变量都会有具体的类型,int,float,char等等,那是否有void类型的变量呢?大家可以动手实验一下,答案是:不行,编译会出错。9 |6 T' R8 R+ C
4 x5 D E. {# |
上图很明显编译器不允许定义void类型的变量,变量都是需要占用一定内存的,既然void表示无类型,编译器自然也就不知道该为其分配多大的内存,于是造成编译失败。# u* T6 ~, |/ L& M2 E, v# z2 \
虽然void不能直接修饰变量,但是其可以用于修饰指针的指向即无类型指针void*,无类型指针那就有意义了,无类型指针不是一定要指向无类型数据,而是可以指向任意类型的数据。. r3 I7 ~$ T F3 Y
2、void * 基本操作 ! N, z# x: K3 C* {7 B
大家其实在使用动态内存分配的使用就已经遇到了void *的使用,来我们一起看看如下几个标准函数的原型定义:
( \9 S& d4 l0 O' g, K& f, [
+ V/ \3 q/ R7 d! v! t9 ?+ _ 上面这些函数都是与内存操作有关的函数,可能一些小伙伴使用过也不一定知道每个参数的具体类型是什么,这些void*部分的形参所传入的实参都是不需要进行强制类型转化的,所以根本就不需要关注传入指针所指向的具体类型,然而函数所返回的void *一般都需要通过强制类型转化为对应的具体类型,除非你最后所传递的变量也是void*类型。
% g: d, n% U* o 参考void*用法:
% x) K8 M( W- y2 h #include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define NUM 10
/*************************************
* Fuction:了解一下void*的使用
* Author : (公众号:最后一个bug)
*************************************/
int main(int argc, char *argv[]) {
int *p1 = (int *)malloc(NUM*sizeof(int));
int *p2 = (int *)malloc(NUM*sizeof(int));
int i = 0;
//初始化p1
for(i = 0;i < NUM;i++)
{
*(p1+i) = i;
}
//进行内存copy
memcpy(p2,p1,NUM*sizeof(int));
//输出另外一个分配的内存
for(i = 0;i < NUM;i++)
{
printf("%d,",*(p2+i));
}
//释放内存
free(p1);
free(p2);
return 0;
} 运行结果:
; C; l0 f7 _: l- g2 ]0 ~9 o. O9 L
. {! m0 B( H3 T8 k+ I/ ]
3、使用void * 实现无类型数据封装
7 \% m) c* L( |2 m1 v! ` 为了保持文章的完整性,也许这里才是作者最想跟大家介绍的,void*既然如此的灵活一定大有用处,如果仅仅只是用来简单的传递参数似乎有点大材小用,我们得把其用到上层的软件设计上来。
) b$ U* E u: G3 Y5 q# W 在一些项目中经常看到有小伙伴把数据类型转来转去,甚至有时候为了一个数据类型的变化还得重新写一个仅仅数据类型不同的函数,这样的代码上万行代码指日可待,按下面我们以一个例子来跟大家介绍一种办法能够减少数据类型变化所带来的程序重复代码的增加。' F$ G/ g0 b" F4 b$ r( G# i" P
参考实例:4 o1 p4 H" `5 ]% C4 |* w: E
#include <stdio.h>
#include <stdlib.h>
/**********************************
* Fuction : add
* descir : 加法的相关数据及处理办法
**********************************/
typedef struct _tag_Add
{
int a;
int b;
int result;
}sAdd;
void Add(void *param)
{
sAdd *p = (sAdd *) param;
p->result = p->a + p->b;
}
/**********************************
* Fuction : add
* descir : 乘法的相关数据及处理办法
**********************************/
typedef struct _tag_Mul
{
float a;
float b;
float result;
}sMul;
void Mul(void *param)
{
sMul *p = (sMul *) param;
p->result = p->a * p->b;
}
/*************************************
* Fuction : sCal
* descir : 公共的调用接口
************************************/
void sCal(void *param,void *fuc)
{
((void (*)(void*))fuc)(param);
}
/**********************************
* Fuction : main
* descir : 应用接口实例
**********************************/
int main(void)
{
sAdd stAdd;
sMul stMul;
//数据初始化
stAdd.a = 10;
stAdd.b = 20;
stMul.a = 5;
stMul.b = 5;
//接口直接用
sCal(&stAdd,Add);
sCal(&stMul,Mul);
//对应的输出
printf("a + b = %d\n",stAdd.result);
printf("a * b = %f\n",stMul.result);
printf("公众号:最后一个bug\n");
return 0;
} 运行结果:
* W/ Q7 M4 Q u6 \
2 {/ _' b. t. A& ^' m/ {: e 分析一下:% {- i3 A' E3 L
上面的例子可能还是无法完全彰显void*的强悍之处了,不过其主要的作用就是为了隐藏数据类型,大家也可以理解为一种数据类型的抽象处理,这也是面向对象编程的一种体现。1 u4 R0 T$ z( ~" b8 o
4、最后小结 " N Z/ w9 P5 d/ \4 a, a. y$ F
对于void这个关键字的使用就先讲这么多吧!大家一定要记得对于一些编程技巧一定要尝试着去使用,可能达到项目目标的方式有很多种,但是一些好的设计不仅仅会让你的代码增色不少,同时也会让同事们觉得你是一个喜欢专研技术的人。