C语言使用void *类型作为函数传参

news/2024/7/8 7:43:58 标签: c语言, 开发语言, c++

C语言使用void *怎么理解:

根据本人的理解,他就是指向操作数据区的首地址而已

凡是void指的数据区都要进行第二次初始化数据类型(即dtype p=(dtype)pdata)*。
举两个例子:

传入函数:

void tx_data(void *pdata)
{
    int* p=(int*)pdata;//第二次初始化
    //接下来对p操作


}

传出函数:

static int data
void* rx_data(void )
{
   
   
   return &data;
}
//使用void *
int* p=(int*)rx_data();  //第二次初始化
//接下来对p操作

隐藏数据类型使用void*,就是这么简单,不要跟什么抽象挂钩,什么无类型就是任意类型,这种理解都是错的,完全是愚弄人
用void的好处:
1.隐藏数据类型,传参让别人不知传的啥,有保密的作用
2.隐藏数据类型,象元编程一样,等数据传过来再识别,再操作,可以简化代码,让代码输入量抽象起来,
3.隐藏数据类型,等数据传过来再识别,再操作,方便给用户堆代码,象linux操作系统的驱动程序都是用void
来给你堆
代码,这样可以让有限的代码转换无限的经济价值
4.隐藏数据类型,你在做各种通讯收发程序时,代码可以重用度高,修改方便,上面的int换成多个stuct的拼接嵌套就可以写出很复杂的程序

后面搞一简单的练兵,void*是随你写程序的功力对他的认识逐步提升的,菜鸟不用急,不积跬步无以至千里

典型的如内存操作函数memcpy和memset的函数原型分别为:

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

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是char *,那才叫真的奇怪了!这样的memcpy和memset明显不是一个“纯粹的,脱离低级趣味的”函数!

下面的代码执行正确

memset接受任意类型指针

int intarray[100];
memset ( intarray, 0, 100*sizeof(int) ); //隐藏数据类型byte的字节操作 第二次初始化

memcpy接受任意类型指针

int intarray1[100], intarray2[100];
memcpy ( intarray1, intarray2, 100*sizeof(int) ); //隐藏数据类型byte的字节操作 第二次初始化

使用枚举定义隐藏数据类型

enum datatype{//枚举出来的都是常量,首字母大写,且作用域为整个main函数
    _Char,
    _CharArr,
    _Int,
    _Float
};
//使用结构体定义一些数据
struct demo{
        char a;
        char stringArray[100];
        int number;
        float score;
    }DEMO;


//使用枚举定义数据结构体数据类型长度
enum datalegth{//此处为了方便观察就不使用首字母大写
    _aLEN = sizeof(char),
    _stringArrayLEN = 20 * sizeof(char),
    _numberLEN = sizeof(int),
    _floatLEN = sizeof(float)
};

测试函数声明

int test(void *data,enum datatype type,int datalength);

测试函数实现

int test(void *data,enum datatype type,int datalength){
    switch(type){
    //第二次初始化
        case _Char:
            {//char *convertData = (char *)malloc(sizeof(char *));//_Char:后面接的是完整语句,不能出现声明,有两种解决办法,一种是在冒号后加分号,但这里是switch中,需要用大括号括起来(代码块,听说源于lisp)

            char *convertData = data;//将void *类型赋值给 char *类型

            char char1 = *convertData;
            printf("its a char: %c\n",char1);
            // free(convertData);
            break;}

        case _CharArr:
            {
                char *convertData = data;
                char charArr[datalength];
                strcpy(charArr,convertData);
                printf("its a string :%s\n",charArr);
                break;
            }
        case _Int:
            {
                int *number = data;
                printf("its a number:%d\n",*number);
                break;
            }
        case _Float:
            {
                float *convertData = data;
                printf("its a float:%f\n",*convertData);
                break;
            }
    }



    return 0;//为了便于添加功能,这里暂时留着
}

结构体变量赋值

DEMO.a = 'A';
    strcpy(DEMO.stringArray,"this is DEMO string variable");//strcpy有一个问题,只管复制进去的,但之前结构体中数组并未初始化可能导致后续//字符出现"粘连"如ble后面不是\0,而是之前堆栈弃用的内存空间的一些垃圾值
    // DEMO.array = "this is DEMO struct variable";//数组只有在初始化时才能这样赋值,之后通过遍历写入,或者strcpy进去
    DEMO.number = 88;
    DEMO.score = 100.0;
//使用结构体指针
struct demo *pDemo = NULL;//指针需要初始化,否则随机指向不可读写的内存区域,后续无法修改指针变量。
pDemo = &DEMO;
//函数调用
test(&(pDemo->a),_Char,_aLEN);  第二次初始化
test(&(pDemo->stringArray),_CharArr,_stringArrayLEN);
test(&(pDemo->number),_Int,_numberLEN);
test(&(pDemo->score),_Float,_floatLEN);
最终代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

enum datatype{//枚举出来的都是常量,首字母大写,且作用域为整个main函数
    _Char,
    _CharArr,
    _Int,
    _Float
};

enum datalegth{//此处为了方便观察就不使用首字母大写
    _aLEN = sizeof(char),
    _stringArrayLEN = 100 * sizeof(char),
    _numberLEN = sizeof(int),
    _floatLEN = sizeof(float)
};

int test(void *data,enum datatype type,int datalength);

int main(int argc,char const argv[]){
    struct demo{
        char a;
        char stringArray[100];
        int number;
        float score;
    }DEMO;

    DEMO.a = 'A';
    strcpy(DEMO.stringArray,"this is DEMO string variable");//strcpy有一个问题,只管复制进去的,但之前结构体中数组并未初始化可能导致后续的字符出现"粘连"如ble后面不是\0,而是之前堆栈弃用的内存空间的一些垃圾值
    // DEMO.array = "this is DEMO struct variable";//数组只有在初始化时才能这样赋值,之后通过遍历写入,或者strcpy进去
    DEMO.number = 88;
    DEMO.score = 100.0;

    struct demo *pDemo = NULL;//指针需要初始化,否则随机指向不可读写的内存区域,后续无法修改指针变量。

    int length = sizeof(struct demo *);//结构体指针大小
    printf("length is %d\n",length);

    // pDemo = (struct demo *)malloc(sizeof(struct demo *));
    // if(pDemo == NULL) printf("分配内存失败!\n");//如果未分配内存     //malloc是分配内存块,C语言的变量名,函数名皆为符号,符号不占用空间,所以将通过malloc获取的内存空间,赋值给指针,实质上是赋值该内存块的首地址给指针,malloc有一个特性,不会将分配的内存块上的数据擦洗掉!!
    pDemo = &DEMO;



    printf("-------\n");
    test(&(pDemo->a),_Char,_aLEN);
    test(&(pDemo->stringArray),_CharArr,_stringArrayLEN);
    test(&(pDemo->number),_Int,_numberLEN);
    test(&(pDemo->score),_Float,_floatLEN);

    // free(pDemo);
    puts("finished prosess");
    return 0;
}

int test(void *data,enum datatype type,int datalength){
    switch(type){
    //第二次初始化
        case _Char:
            {//char *convertData = (char *)malloc(sizeof(char *));//_Char:后面接的是完整语句,不能出现声明,有两种解决办法,一种是在冒号后加分号,但这里是switch中,需要用大括号括起来(代码块,听说源于lisp)

            char *convertData = data;//将void *类型赋值给 char *类型

            char char1 = *convertData;
            printf("its a char: %c\n",char1);
            // free(convertData);
            break;}

        case _CharArr:
            {
                char *convertData = data;
                char charArr[datalength];
                strcpy(charArr,convertData);
                printf("its a string :%s\n",charArr);
                break;
            }
        case _Int:
            {
                int *number = data;
                printf("its a number:%d\n",*number);
                break;
            }
        case _Float:
            {
                float *convertData = data;
                printf("its a float:%f\n",*convertData);
                break;
            }
    }



    return 0;//为了便于添加功能,这里暂时留着
}

简单的void* 返回int* 类型的函数和一个返回char* 类型的函数

#include <stdlib.h>
#include <stdio.h>
void reInt(int);
void* reIntp(int*);
void* reChar(char*);
int main()
{
	int num=10;
	int *nump;
	char str[10]="CSDN";
	char* strp;
	reInt(num);
	nump = (int*) reIntp(&num); //强制类型转化不能忘!隐藏数据类型  第二次初始化
	strp = (char*)reChar(str); //强制类型转化不能忘! 隐藏数据类型  第二次初始化
	printf("主函数输出:%d\n",*nump);
	printf("主函数输出:%s\n",strp);
	return 0;
}
//一般返回类型的函数
void reInt(int a)
{
	printf("void返回类型的函数的输出:%d\n",a);
	return; // 没有返回值
}
//void*返回类型的函数 返回int*
void* reIntp(int *a)
{
	printf("void*返回类型返回int*的函数的输出:%d\n", *a);
	return a; // 返回 int *隐藏数据类型
}
//void*返回类型的函数 返回char*
void* reChar(char* str)
{
	printf("void*返回类型返回char*的函数的输出:%s\n",str);
	return str;隐藏数据类型
}

void*函数的返回值类型struct

struct MyStruct {
    int a;
    char b;
    double c;
};
 
void* get_struct() {
    struct MyStruct* s = malloc(sizeof(struct MyStruct));
    s->a = 1;
    s->b = 'b';
    s->c = 3.0;
    return s;
}
 
int main() {
    struct MyStruct* s = (struct MyStruct*)get_struct();  第二次初始化
    printf("a: %d, b: %c, c: %f\n", s->a, s->b, s->c);
    free(s);
    return 0;
}

创作不容易,如果对您有帮助,请多打赏!!!!


http://www.niftyadmin.cn/n/5536842.html

相关文章

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…

2.5 C#视觉程序开发实例1----开始设计架构一个简单的程序

2.5 C#视觉程序开发实例1----开始设计架构一个简单的程序 第一步目标&#xff1a; 1 IO交互&#xff1a;读取IO 并且显示 2 相机采集线程&#xff1a; In[0]上升沿&#xff0c;采集图像&#xff0c; 通知处理线程显示图片 3 图像处理线程&#xff1a; 接受信号&#xff0c;显示…

Map Set(Java篇详解)

&#x1f341; 个人主页&#xff1a;爱编程的Tom&#x1f4ab; 本篇博文收录专栏&#xff1a;Java专栏&#x1f449; 目前其它专栏&#xff1a;c系列小游戏 c语言系列--万物的开始_ 等 &#x1f389; 欢迎 &#x1f44d;点赞✍评论⭐收藏&#x1f496;三连支持…

精通SQL Server端口管理:添加与删除监听端口的指南

引言 SQL Server的端口管理是数据库管理员(DBA)必须掌握的关键技能之一。端口配置不仅关系到数据库的网络通信能力&#xff0c;还直接影响到数据库的安全性和性能。本文将详细介绍如何在SQL Server中添加和删除监听端口&#xff0c;以及相关的配置策略和最佳实践。 SQL Serve…

谷粒商城学习笔记-05-项目微服务划分图

文章目录 一&#xff0c;商城业务服务-前端服务二&#xff0c;商城业务服务-后端服务三&#xff0c;存储服务四&#xff0c;第三方服务五&#xff0c;服务治理六&#xff0c;日志七&#xff0c;监控预警系统1&#xff0c;Prometheus2&#xff0c;Grafana3&#xff0c;Prometheu…

手写SpringMVC之调度器DispatcherServlet

DispatcherServlet&#xff1a;分发、调度 根据上一节&#xff0c;已经实现了将controller的方法添加到容器中&#xff0c;而DispatcherServlet的作用就是接收来自客户端的请求&#xff0c;然后通过URI的组合&#xff0c;来找到对应的RequestMapping注解的方法&#xff0c;调用…

Ubuntu20.04突然没网的一种解决办法

本来要学一下点云地图处理&#xff0c;用octomap库&#xff0c;但是提示少了octomap-server库&#xff0c;然后通过下面命令安装的时候&#xff1a; sudo apt install ros-noetic-octomap-server 提示&#xff1a;错误:7 https://mirrors.ustc.edu.cn/ubuntu focal-security …

电商数据仓库

1.数据仓库的数据来源为业务数据库&#xff08;mysql&#xff09; 2.通过sqoop将mysql中的业务数据导入到大数据平台&#xff08;hive&#xff09; 3.通过hive进行数据计算和数据分析 形成数据报表 4.再通过sqoop将数据报表导出到mysql 5.使用FineReport制作数据报表 1.数据…