【可变参数列表如何可变?】

news2024/9/20 10:52:47

可变参数列表

本章重点

学会使用可变参数列表的使用与原理

函数传参补充知识

  • 如果函数没有形式参数,仍然可以给函数传递参数。
  • 在c语言中,只要发生了函数调用并且传递了函数,必定形成临时变量。
  • 所谓的临时拷贝本质就是在栈帧内部形成的,从右向左依次形成临时拷贝(变量)。

求两个数据中的最大值

#include <stdio.h>
//求两个数据中的最大值
int FindMax(int x, int y)
{
    if (x > y) {
        return x;
    }
    return y;
}
int main()
{
    int x = 0;
    int y = 0;
    printf("Please Eneter Two Data# ");
    scanf("%d %d", &x, &y);
    int max = FindMax(x, y);
    printf("max = %d\n", max);
    return 0;
}
  • 如果未来我们的需求变了,不再求固定的数据个数的最大值
  • 而是求任意多个数据中的最大值(至少一个),要求不能使用数组
  • 因为目前参数个数不确定,那么函数编写的时候,参数个数也无法确定,换句话说,函数也就没法编写
  • 不过,C提供了满足该场景的解决方案:可变参数列表

#include <stdio.h>
#include <windows.h>
//num:表示传入参数的个数
//可变参数列表至少有一个参数
int FindMax(int num, ...)//可变参数列表
{
	va_list arg; //定义可以访问可变参数部分的变量,其实是一个char*类型
	va_start(arg, num); //使arg指向可变参数部分
	int max = va_arg(arg, int); //根据类型,获取可变参数列表中的第一个数据
	int i = 0;
	for (i = 0; i < num - 1; i++) {//获取并比较其他的
		int curr = va_arg(arg, int);
		if (max < curr) {
			max = curr;
		}
	}
	va_end(arg); //arg使用完毕,收尾工作。本质就是将arg指向NULL
	return max;
}
int main()
{
	int max = FindMax(5, 11, 22, 33, 44, 55);
	printf("max = %d\n", max);//55
	system("pause");
	return 0;
}
  1. 使用va_list类型的变量声明一个可变参数列表的访问指针,通常命名为arg

  2. 使用va_start(arg, last_fixed_param)宏来初始化可变参数列表。last_fixed_param是最后一个固定参数的名称,也就是num,在这个参数之后是可变数量的参数。这个宏会将arg指针指向可变参数列表的起始位置。

  3. 使用va_arg(arg, type)宏来获取可变参数列表中的参数值。type是参数的数据类型。该宏从可变参数列表中获取下一个参数的值,并将arg指针移动到下一个参数的位置。

  4. 使用va_end(arg)宏来结束对可变参数列表的访问。这个宏执行一些必要的清理工作,并将arg指针置为NULL

如果将参数改成char类型,求char类型变量中的最大值,代码会有问题吗? 

#include <stdio.h>
#include <windows.h>
//num:表示传入参数的个数
int FindMax(int num, ...)
{
	va_list arg; //定义可以访问可变参数部分的变量,其实是一个char*类型
	va_start(arg, num); //使arg指向可变参数部分
	int max = va_arg(arg, int); //根据类型,获取可变参数列表中的第一个数据
	for (int i = 0; i < num - 1; i++) {//获取并比较其他的
		int curr = va_arg(arg, int);//va_arg(arg, char),这是不正确的
		if (max < curr) {
			max = curr;
		}
	}
	va_end(arg); //arg使用完毕,收尾工作。本质就是讲arg指向NULL
	return max;
}
int main()
{
	char a = '1'; //ascii值: 49
	char b = '2'; //ascii值: 50
	char c = '3'; //ascii值: 51
	char d = '4'; //ascii值: 52
	char e = '5'; //ascii值: 53
	int max = FindMax(5, a, b, c, d, e);
	printf("max = %d\n", max);
	system("pause");
	return 0;
}

先来解释一下汇编代码中的movsx是什么意思?

通过查看汇编,我们看到,在可变参数场景下:

1. 实际传入的参数如果是char,short,float,编译器在编译的时候,会自动进行提升(通过movsx指令进行到当前计算机寄存器的位数)

2. 函数内部使用的时候,根据类型提取数据,更多的是通过int或者double来进行。

注意事项

  • 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
  • 参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用 va_start 。
  • 这些宏是无法直接判断实际存在参数的数量。
  • 这些宏无法判断每个参数的类型。
  • 如果在 va_arg 中指定了错误的类型,那么其后果是不可预测的。

原理

  1. 可变参数列表对应的函数,最终调用也是函数调用,也要形成栈帧。
  2. 栈帧形成前,临时变量是要先入栈的,根据之前所学,参数之间位置关系是固定的。
  3. 通过上面汇编的学习,发现了短整型在可变参数部分,会默认进行整形提升,那么函数内部在提取该数据的时候,就要考虑提升之后的值,如果不加考虑,获取数据可能会报错或者结果不正确。

我们接下来就看看这几个宏的含义:

1、va_list arg;

 2、va_start(arg, num);

3、va_end(arg);

现在我们来重点了解一下这个宏#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

前提: 为了后面方便表述,我们假设sizeof(n)的值是n(char 1,short 2, int 4)

        我们在32位平台,vs2013下测试,sizeof(int)大小是4,其他情况我们不考虑

根据上面学到的知识,_INTSIZEOF(n)中的参数可以是变量类型或者变量名。

我们来计算一下参数为char和short,该宏的值为多少?

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

结果都是4个字节 

_INTSIZEOF(n)的意思:计算一个最小数字x,满足 x>=n && x%4==0,其实就是一种4字节对齐的方式

是什么: 比如n是:1,2,3,4 对n进行向 sizeof(int) 的最小整数倍取整的问题 就是 4

                比如n是:5,6,7,8 对n进行向 sizeof(int) 的最小整数倍取整的问题 就是 8

为什么:要有这个4字节对齐:

              结合之前栈帧的学习、上面代码的测试结果和movsx指令我们就可以知道

怎么办到的

 

本章结束啦,再见啦。

 

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

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

相关文章

Tcl常用命令备忘录-基础篇

一、置换 1、变量置换 在Tcl中&#xff0c;$符号可以用来引用变量。变量置换可以用来将变量的值嵌入到代码中。例如&#xff1a; set name "Tom" puts "Hello, $name!"这个示例中&#xff0c;变量$name的值会在输出语句中被替换为"Tom"。 2…

腾讯云服务器镜像市场快速搭建WordPress博客网站教程

通过腾讯云服务器的镜像市场搭建WordPress网站非常简单&#xff0c;不需要手动配置WP所需的Web环境&#xff0c;一键即可安装WordPress博客&#xff0c;腾讯云百科使用腾讯云服务器通过镜像市场的WordPress镜像搭建WP网站教程&#xff1a; 目录 腾讯云服务器通过市场镜像安装…

SpringMvc拦截器和手写模拟SpringMvc工作流程源码详解

目录 1. SpringMvc简介 1.1 什么是MVC 1.2 什么是SpringMvc 1.3 SpringMvc 能干什么 1.4 SpringMvc 工作流程 2. SpringMvc拦截器和过滤器 2.1 拦截器 2.1.1 拦截器作用 2.1.2 拦截器和过滤器的区别 2.1.3 拦截器方法说明 2.1.4 多个拦截器执行顺序 2.1.5 自定义拦…

29 虚拟地址到物理地址的转换

前言 呵呵 这是 linux 中内存管理中很基础的一环 用户程序 操作的地址都是虚拟地址, 虚拟地址通过 mmu 转换为物理地址 用户程序 看到的地址都是一个完整的世界, 只有具体需要使用的时候 产生缺页中断, 然后 分配具体的物理页 这里 要说的就是 虚拟地址 到 物理地址 的转…

1001router6-react

文章目录 1 一级路由2 Navigate3 NavLink 自定义高亮样式4 useRoutes()5 嵌套路由6 路由传参6.1 传递params参数6.2 传递search参数6.3 传递state参数 7 编程式导航7.1 路由跳转7.2 前进、后退 8 钩子函数8.1 useInRouterContext()8.2 useNavigationType()8.3 useOutlet()8.4 u…

手写Spring框架---AOP实现

目录 容器是OOP的高级工具 系统需求 关注点分离Concern Separation 原有实现 AOP的成员 Advice的种类 单个Aspect的执行顺序 多个Aspect的执行顺序 Introduction-引入型Advice 代理模式 JDK动态代理 Spring AOP的实现原理之JDK动态代理 Spring AOP的实现原理之CGL…

CSS基础学习--19 下拉菜单

一、基本下拉菜单 当鼠标移动到指定元素上时&#xff0c;会出现下拉菜单 <!DOCTYPE html> <html> <head> <title>下拉菜单实例</title> <meta charset"utf-8"> <style> .dropdown {position: relative;display: inline-…

UnityVR-项目的管理阶层

目录 概述 项目的总体架构 单例基类 继承MonoBehaviour的单例基类 概述 一个具备一定规模的项目&#xff0c;一般都需要由不同人员合作完成&#xff0c;每个人的想法风格不相同&#xff0c;如果一开始没有定下基本的框架&#xff0c;会让实现时混乱不堪&#xff0c;而且无法…

【CesiumJS入门】(5)GooJSON的加载、更新、监听与销毁——GeoJsonDataSource应用

前言 本篇&#xff0c;我们将较完整得介绍Cesium中GeoJSON/TopoJSON相关的方法。 GeoJSON规范地址&#xff1a;RFC 7946: The GeoJSON Format (rfc-editor.org) GeoJSON在线绘制&#xff1a;geojson.io CesiumJS提供了一个名为DataSource的类&#xff0c;它主要是用来加载和展…

Java-API简析_java.util.UUID类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/131270140 出自【进步*于辰的博客】 其实我的【Java-API】专栏内的博文对大家来说意义是不大的。…

《计算之魂》读书笔记——递归与堆栈的关系

进入梅雨季节&#xff0c;一周末雨水连绵不绝&#xff0c;空气中泛着潮湿的凉爽。这个天气最适合找个角落&#xff0c;安安静静地读书写字。 继续读《计算之魂》&#xff0c;前次读到递归&#xff0c;今天则了解递归地数据结构实现。递归算法的层层实现&#xff0c;需要保留从…

大数据周会-本周学习内容总结018

开会时间&#xff1a;2023.06.18 15:00 线下会议 01【调研-数据分析&#xff08;质量、ETL、可视化&#xff09;】 ETL&#xff0c;是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform…

Tcl常用命令备忘录-正则命令篇

正则表达式是一种用于匹配、查找、替换文本中特定模式的工具。在Tcl脚本中&#xff0c;可以使用正则表达式对字符串进行匹配、查找和替换。 regexp 语法&#xff1a; regexp ?选项? 正则表达式 字符串 ?变量1 变量2 ...? 其中&#xff0c;?选项?为可选项&#xff0c;…

基于蒙特卡洛法的规模化电动汽车充电负荷预测(PythonMatlab实现)

目录 0 概述 1 蒙特卡洛模拟方法介绍 2 规模化电动汽车充电负荷预测计算方法 3 完整代码 0 概述 对于本文的研究,依据不同用途电动汽车影响因素的分布函数和设定参数&#xff0c;采用蒙特卡洛法,对各用途电动汽车的日行驶里程、起始充电时间概率分布参数进行随机抽样&#xff0…

linuxOPS系统服务_Linux下软件的安装方式之源码安装

Linux下有哪些软件安装方式 ① RPM软件包管理&#xff08;软件名称.rpm&#xff09; ② YUM软件包管理&#xff08;使用yum命令install 软件名称&#xff09; > 下载 安装一体化 ③ 源码编译安装&#xff08;相对来说是最复杂的一种方式&#xff09; 软件包类型 ☆ 二…

十二、docker学习-docker核心docker网络之bridge网络(2)

bridge网络 bridge网络表现形式就是docker0这个网络接口。容器默认都是通过docker0这个接口进行通信。也可以通过docker0去和本机的以太网接口连接&#xff0c;这样容器内部才能访问互联网。 # 查看docker0网络&#xff0c;在默认环境中&#xff0c;一个名为docker0的linux b…

Go语言的TCP和HTTP网络服务基础

目录 【TCP Socket 编程模型】 Socket读操作 【HTTP网络服务】 HTTP客户端 HTTP服务端 TCP/IP 网络模型实现了两种传输层协议&#xff1a;TCP 和 UDP&#xff0c;其中TCP 是面向连接的流协议&#xff0c;为通信的两端提供稳定可靠的数据传输服务&#xff1b;UDP 提供了一种…

NodeJSMongodbMVC管理开发⑨

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言MVC思想开发 服务器代码演示 M层 Services 或 Model V层 Views C层 Controllers总结 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c…

C语言笔记之结构体总结

C语言笔记之结构体总结 code review! 文章目录 C语言笔记之结构体总结一.介绍二.3种结构体类型变量说明1. 先定义结构&#xff0c;再定义结构变量2. 定义结构体类型的同时说明变量3. 直接说明结构变量(匿名结构体) 四.结构体成员表示方法五.结构体指针做参数六.结构体初始化1…

阵列信号处理笔记(2):均匀线阵、均匀加权线阵、波束方向图

阵列信号处理笔记&#xff08;2&#xff09; 文章目录 阵列信号处理笔记&#xff08;2&#xff09;均匀线阵&#xff08;Uniform Linear Array&#xff09;均匀加权线阵波束方向图的关键参数附polardb.m用来计算HPBW的Mathematica代码&#xff0c;以及用于拟合的数据拟合的MATL…