C语言可变参数与内存管理

news2024/9/23 23:32:46

有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数。下面的实例演示了这种函数的定义。

int func(int, ... ) 
{
   .
   .
   .
}
 
int main()
{
   func(2, 2, 3);
   func(3, 2, 3, 4);
}

请注意,函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:

  • 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
  • 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
  • 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
  • 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
  • 使用宏 va_end 来清理赋予 va_list 变量的内存。

现在让我们按照上面的步骤,来编写一个带有可变数量参数的函数,并返回它们的平均值:

#include <stdio.h>
#include <stdarg.h>
 
double average(int num,...)
{
 
    va_list valist;
    double sum = 0.0;
    int i;
 
    /* 为 num 个参数初始化 valist */
    va_start(valist, num);
 
    /* 访问所有赋给 valist 的参数 */
    for (i = 0; i < num; i++)
    {
       sum += va_arg(valist, int);
    }
    /* 清理为 valist 保留的内存 */
    va_end(valist);
 
    return sum/num;
}
 
int main()
{
   printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
   printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}

当上面的代码被编译和执行时,它会产生下列结果。应该指出的是,函数 average() 被调用两次,每次第一个参数都是表示被传的可变参数的总数。省略号被用来传递可变数量的参数。

Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

--------------------------------------------------------------------------------------------------------------------------------

C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。

注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。 

动态分配内存

编程时,如果您预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳 100 个字符,所以您可以定义数组,如下所示:

char name[100];

但是,如果您预先不知道需要存储的文本长度,例如您想存储有关一个主题的详细描述。在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
   char name[100];
   char *description;
 
   strcpy(name, "Zara Ali");
 
   /* 动态分配内存 */
   description = (char *)malloc( 200 * sizeof(char) );
   if( description == NULL )
   {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   }
   else
   {
      strcpy( description, "Zara ali a DPS student in class 10th");
   }
   printf("Name = %s\n", name );
   printf("Description: %s\n", description );
}

当上面的代码被编译和执行时,它会产生下列结果:

Name = Zara Ali
Description: Zara ali a DPS student in class 10th

上面的程序也可以使用 calloc() 来编写,只需要把 malloc 替换为 calloc 即可,如下所示:

calloc(200, sizeof(char));

当动态分配内存时,您有完全控制权,可以传递任何大小的值。而那些预先定义了大小的数组,一旦定义则无法改变大小。

重新调整内存的大小和释放内存

当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数 free() 来释放内存。

或者,您可以通过调用函数 realloc() 来增加或减少已分配的内存块的大小。让我们使用 realloc() 和 free() 函数,再次查看上面的实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
   char name[100];
   char *description;
 
   strcpy(name, "Zara Ali");
 
   /* 动态分配内存 */
   description = (char *)malloc( 30 * sizeof(char) );
   if( description == NULL )
   {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   }
   else
   {
      strcpy( description, "Zara ali a DPS student.");
   }
   /* 假设您想要存储更大的描述信息 */
   description = (char *) realloc( description, 100 * sizeof(char) );
   if( description == NULL )
   {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   }
   else
   {
      strcat( description, "She is in class 10th");
   }
   
   printf("Name = %s\n", name );
   printf("Description: %s\n", description );
 
   /* 使用 free() 函数释放内存 */
   free(description);
}

当上面的代码被编译和执行时,它会产生下列结果:

Name = Zara Ali
Description: Zara ali a DPS student.She is in class 10th

您可以尝试一下不重新分配额外的内存,strcat() 函数会生成一个错误,因为存储 description 时可用的内存不足。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/132098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode题解 二叉树(八):404 左叶子之和;513 找树左下角的值;112 路径总和;113 路径总和II

二叉树 404 左叶子之和 easy 左叶子结点也好判断&#xff0c;若某结点属于左结点&#xff0c;且无子树&#xff0c;就是左叶子结点 即也如此&#xff0c;所以如果要判断&#xff0c;必然要从父结点下手&#xff0c;涉及到三层结点的处理 如果要使用递归法&#xff0c;要使用…

(二十三)大白话数据库服务器上的RAID存储架构的电池充放电原理

文章目录 1、RAID卡的缓存2、RAID卡的缓存里的数据会突然丢失怎么办?3、锂电池存在性能衰减问题1、RAID卡的缓存 服务器使用多块磁盘组成的RAID阵列的时候,一般会有一个RAID卡,这个RAID卡是带有一个缓存的,这个缓存不是直接用我们的服务器的主内存的那种模式,他是一种跟内…

网络静态路由综合实验

1.首先分配ip,配置ip和环回 [Huawei]sysname R1 [R1]interface LoopBack 0 [R1-LoopBack0]ip add 192.168.1.33 28 [R1-LoopBack0]q [R1]int l 1 [R1-LoopBack1]ip add 192.168.1.49 28 [R1-LoopBack1]q [R1]int g 0/0/0 [R1-GigabitEthernet0/0/0]ip add 192.168.1.1 30 [R1-…

JVM与Java体系结构

目录 前言 架构师每天都在思考什么&#xff1f; Java vs C Java生态圈 字节码 多语言混合编程 虚拟机与Java虚拟机 虚拟机 Java虚拟机 JVM的位置 JVM整体结构 Java代码执行流程 JVM的架构模型 举例 字节码反编译 总结 栈 JVM生命周期 虚拟机的启动 虚拟机的…

时间从来不语,确回答了所有问题——我的2022年终总结

趁着没阳&#xff0c;趁着电脑还能开机&#xff0c;趁着还能写&#xff0c;赶紧小结过去这一年。没有别的感觉&#xff0c;就是感觉太快&#xff0c;时间太过匆匆.....最大的感触是两个字“变化”&#xff0c;如果非要说四个字是“变化太快”&#xff0c;就如当下的yi情政策&am…

多线程_进阶

文章目录线程通信概念使用方式案例单例模式阻塞式队列线程池常见的锁策略乐观锁 悲观锁CASCAS存在的问题:ABA问题读写锁自旋锁公平锁 非公平锁非公平锁公平锁synchronizedjvm对synchronized的优化:锁升级synchronized的其他优化Lock体系synchronized vs lock独占锁vs共享锁独占…

Arch/Manjaro换源+安装常用的软件+安装显卡驱动

本文将教你&#xff1a;换源安装显卡驱动&#xff0c;安装常用软件例如腾讯会议&#xff0c;QQ&#xff0c;WPS 一起交流Linux知识&#xff0c;欢迎加入Skype群&#xff1a; Join conversationhttps://join.skype.com/q6wrF3d6Usni pacman换清华源 首先安装vim&#xff0c;用来…

(二十四)大白话RAID锂电池充放电导致的MySQL数据库性能抖动的优化

案例实战:RAID锂电池充放电导致的MySQL数据库性能抖动的优化 文章目录 1、磁盘故障怎么保障数据不丢失?2、线上MySQL数据库的性能定期抖动的原因1、磁盘故障怎么保障数据不丢失? 前面经过了几天的生产经验的一些铺垫,包括MySQL磁盘读写的机制,Linux存储系统的原理,RAID磁…

垃圾佬图拉丁装机

理论知识 缩线程 amd搞了个推土机架构 两个核心公用一个浮点运算单元&#xff0c;因为浮点运算只占百分之二十。 浮点运算应该交给更适合的gpu去做 好的对比 RDP 微软的RDP本身就定位是一个远程登录和维护windows系的工具&#xff0c;它为什么要支持管理别的系统&#xff…

【mybatis generator实战】 1.crud 2.计数 3.自定义复杂mapper代码组织

1.计数 2.CRUD 增 注意&#xff1a; insert&#xff1a;一个必须全部有值。 insertSelective是&#xff1a;部分有值就行&#xff0c;用的较多。 有疑问可以看源码&#xff0c;发现xxxSelective就是拼接了一些参数。 删 改 注意&#xff1a; 4个更新方法&#xff1a; …

QML学习笔记【06】:QML与C++交互

1 QML端直接调用C端变量及函数 1、 创建继承自QObject的C类&#xff0c;对象必须继承自QObject才能在QML被使用和访问 2、在类定义中使用Q_PROPERTY导出成员的READ、WRITE、NOTIFY接口&#xff0c;这样类中的成员变量就可以在QML调用和修改了&#xff0c;同时变量被修改后也会…

剑指 Offer 18. 删除链表的节点

一、题目描述 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。 返回删除后的链表的头节点。 示例 1: 输入: head [4,5,1,9], val 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点&#xff0c;那么在调用了你的函数之后&#xf…

2022年最后一篇推文 | C语言编程十诫

正文大家好&#xff0c;我是bug菌~2022年最后一篇推文原本选择一篇年终总结会比较合适&#xff0c;然而坐在窗台&#xff0c;望着窗外思索了良久&#xff0c;与往年总结有点不同&#xff0c;这个时间节点有着他的特殊性&#xff0c;不出意外&#xff0c;明年会有非常多的变化、…

OpenShift 4 - 用 HyperShift 实现以“托管集群”方式部署运行 OpenShift 集群

《OpenShift / RHEL / DevSecOps / Ansible 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.11 ACM 2.6 AWS 环境中验证 文章目录用 HyperShift 实现 OpenShift 托管集群什么是 HyperShift 托管集群以及架构HyperShift 托管集群的价值成本优势部署优势管理优势在 RHACM …

【Kaggle】Global Wheat Detection

代码链接 实验目的 小麦来自世界各地。密度的小麦植株经常重叠&#xff1b;风会使得照片模糊&#xff1b;外观会因成熟度&#xff0c;颜色&#xff0c;基因型和头部方向而异。使用图像处理和目标检测完成小麦头的位置的标定。完成训练并现场验证后上传指定的输出文件进行验证…

大数据NiFi(三):NiFi关键特性

文章目录 NiFi关键特性 一、​​​​​​​​​​​​​​流管理

人工智能轨道交通行业周刊-第28期(2022.12.26-2023.1.1)

本期关键词&#xff1a;NOCC、车站闸机、雾闪、2022年度盘点、智慧园区 1 整理涉及公众号名单 1.1 行业类 RT轨道交通中关村轨道交通产业服务平台人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通Rai…

安全—01day

DNS 域名解析过程: 1.浏览器首先查询浏览器的缓存&#xff0c;因为浏览器会按照一定的频率缓存 DNS 记录 2.若浏览器无缓存&#xff0c;那么查询操作系统的 HOST 文件&#xff0c;查询是否有 DNS 记录。 3.若还没有命中域名&#xff0c;就请求本地域名服务器该服务器一般都会缓…

Qt音视频开发07-合并音视频文件

一、前言 之前已经把音视频分开存储了对应的文件&#xff0c;因为这个需求特别少&#xff0c;当然确实有部分用户是需要把音视频分开存储&#xff0c;但是毕竟是很少数&#xff0c;绝大部分的用户都是音视频合并到一个MP4文件&#xff0c;所以如果要合并到一个文件&#xff0c…

二进制与十六进制的相互转换;按位操作:与() 和 或(|)

目录 一、二进制转换十六进制 二、十六进制转换二进制 三、按位操作&#xff1a;与&#xff08;&&#xff09; 和 或&#xff08;|&#xff09; 1、按位与&#xff08;&&#xff09;操作 2、按位或&#xff08;|&#xff09;操作 得出结论&#xff1a;清0用与&am…