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

简介: 【可变参数列表如何可变?】

本章重点


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


函数传参补充知识


  • 如果函数没有形式参数,仍然可以给函数传递参数。
  • 在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指令我们就可以知道


怎么办到的:


相关文章
|
2月前
|
存储 C语言 索引
怎样定义字符数组
字符数组是C语言和其他一些编程语言中用于存储字符序列的数据结构。字符数组可以存储一个或多个字符,并通过索引访问每个字符。下面我们将详细解释如何定义字符数组,并附上相应的代码示例。
68 0
元组作为函数参数
在 Julia 中,示例展示了如何以元组作为函数参数。定义函数`testFunc(x, y, z; a=10, b=20, c=30)`,它接受位置参数及可变参数。创建元组`options = (b = 200, c = 300)`,然后调用函数`testFunc(1, 2, 3; options...)`,输出显示元组中的参数值。如果在元组后指定参数,如`b = 1000_000`,则会覆盖元组中的`b`值。执行不同参数顺序的函数调用,展示了参数覆盖的行为。
|
2月前
可变参数传入数组
可变参数传入数组
25 0
|
8月前
|
Cloud Native Go C++
你以为传切片就是传引用了吗?
你以为传切片就是传引用了吗?
|
11月前
|
Java
Java方法的重载、可变个数形参、方法参数的值传递机制
Java方法的重载、可变个数形参、方法参数的值传递机制
74 0
数组作为函数参数传参&数组名到底代表什么意义?
数组作为函数参数传参&数组名到底代表什么意义?
|
Python
Python关键字个数可变的位置传参与个数可变的关键字传参
Python关键字个数可变的位置传参与个数可变的关键字传参
62 0
|
机器学习/深度学习 PHP 开发者
可变函数|学习笔记
快速学习可变函数
85 0
可变函数|学习笔记
|
C语言 索引
数组的知识(可变与不可变)
数组的知识(可变与不可变)
409 0
数组的知识(可变与不可变)
|
JSON 数据格式
解构(数组,对象,字符串,数值,布尔,函数参数)
解构(数组,对象,字符串,数值,布尔,函数参数)https://es6.ruanyifeng.com/#docs/destructuring
120 0