分享一个2015年华为笔试知识点:变长参数函数

变长参数的函数即参数个数可变、参数类型不定 的函数。

设计一个参数个数可变、参数类型不定的函数是可能的,最常见的例子是printf函数、scanf函数和高级语言的Format函数。在C/C++中,为了通知编译器函数的参数个数和类型是可变的(即是不定的、未知的),就必须以三个点结束该函数的声明。

int printf(const char * _Format, ...);

int scanf(const char * _Format, ...);

int func(int a,int b,...);

上面func函数的声明指出该函数至少有两个整型参数和紧随其后的0个或多个类型未知的参数。在C/C++中,任何使用变长参数声明的函数都必须至少有一个指定的参数(又称强制参数),即至少有一个参数的类型是已知的,而不能用三个点省略所有参数的指定,且已知的指定参数必须声明在函数最左端。

int func(...);//wrong

int func(...,int a);//wrong

Variadic functions are functions (e.g. std::printf) which take a variable number of arguments.

To declare a variadic function, an ellipsis is used as the last parameter, e.g. int printf(const char* format, ...);. See Variadic arguments for additional detail on the syntax, automatic argument conversions and the alternatives.

To access the variadic arguments from the function body, the following library facilities are provided(Defined in header ):

//访问可变参数流程

va_list args; //定义一个可变参数列表

va_start(args,arg);//初始化args指向强制参数arg的下一个参数;

va_arg(args,type);//获取当前参数内容并将args指向下一个参数

...//循环获取所有可变参数内容

va_end(args);//释放args

含有变长参数的函数是怎么实现的呢?变长参数函数的实现其实关键在于怎么使用参数,指定了的参数好说,直接使用指定的参数名称访问,但未指定的参数呢?我们知道函数调用过程中参数传递是通过栈来实现的,一般调用都是从右至左的顺序压参数入栈,因此参数与参数之间是相邻的,知道前一个参数的类型及地址,根据后一个参数的类型就可以获取后一个参数的内容。对于变长参数函数,结合一定的条件,我们可以根据最后一个指定参数获取之后的省略参数内容。如,对于函数func,我们知道了参数b的地址及类型,就可知道第一个可变参数的栈地址(如果有的话),如果知道第一个可变参数的类型,就可知道第一个可变参数的内容和第二个可变参数的地址(如果有的话)。以此类推,可以实现对可变参数函数的所有参数的访问。

//sum为求和函数,其参数类型都为int,但参数个数不定

//第一个参数(强制参数)n指定后面有多少可变参数

int sum(unsigned int n,...)

{

int sum=0;

va_list args;

va_start(args,n);

while(n>0)

{

//通过va_arg(args,int)依次获取参数的值

sum+=va_arg(args,int);

n--;

}

va_end(args);

return sum;

}

那么,要怎么指定上诉的“一定的条件”呢?最简单的方法就像printf等函数一样,使用格式化占位符。分析格式化字符串参数,通过事先定义好的格式化占位符可知可变参数的类型及个数,从而获取各个参数内容。一般对于可变参数类型相同的函数也可直接在强制参数中指定可变参数的个数和类型,这样也能获取各个参数的内容。

无论哪种,都涉及对栈地址偏移的操作。结合栈存储模式和系统数据类型的字长,我们可根据可变参数的类型很容易得到栈地址的偏移量。这里简单介绍使用va_start、va_arg、va_end三个标准宏来实现栈地址的偏移及获取可变参数内容。这三个宏定义在stdarg.h头文件中,他们可根据预先定义的系统平台自动获取相应平台上各个数据类型的偏移量。

// ConsoleApplication13.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include

#include

void simple_printf(const char* fmt...)

{

va_list args;

va_start(args, fmt);

while (*fmt != '\0') {

if (*fmt == 'd') {

int i = va_arg(args, int);

std::cout <

}

else if(*fmt=='c')

{

int c = va_arg(args, int);

std::cout <(c) <

}

else if (*fmt == 'f') {

double d = va_arg(args, double);

std::cout <

}

++fmt;

}

va_end(args);

}

int main()

{

simple_printf("dcff",3,'a',1.999,42.5);

}

运行结果:

对于可变参数函数的调用有一点需要注意,实际的可变参数的个数必须比前面强制参数中指定的个数要多,或者不小于,也即后续参数多一点不要紧,但不能少,如果少了则会访问到函数参数以外的堆栈区域,这可能会把程序搞崩掉。前面强制参数中指定的类型和后面实际参数的类型不匹配也有可能造成程序崩溃。

拥有变长参数的函数在声明定义时其参数个数与类型是不定的,在运行调用时参数的状态则是一定的。而默认参数函数在声明定义时其参数类型与个数都是一定的,只是后面部分参数指定了默认值,可通过省略(不指定)部分参数调用这个默认参数函数。但是默认参数函数还是使用了声明中指定的全部参数,只不过编译器做了个顺水人情,自动给后部分参数赋了默认值;而变长参数函数则仅仅使用了运行调用时提供的参数。

在matlab中这一概念是如何体现的呢?

matlab中varargin简介

varargin可以看做“Variable length input argument list”的缩写。在matlab中, varargin提供了一种函数可变参数列表机制。 就是说, 使用了“可变参数列表机制”的函数允许调用者调用该函数时根据需要来改变输入参数的个数。

matlab中很多内建函数和工具箱函数都使用了这种机制。 比如图像处理工具箱中的imshow函数。 该函数允许我们根据图像数据特点来调用:

比如, 显示一张真彩色位图, 我们可以简单的使用:

imshow(RGB), 其中RGB是通过imread函数读取图像获得的图像数据。这里我们只给了一个参数。

但是在显示索引图像时, 因为索引图像使用了调色板,因此为了正确显示图像, 除了图像数据外, 我们还要额外指定显示图像所使用的调色板(一般也由imread函数获得),这样就出现了以下的调用格式:

imshow(X, map)

那么, 这种机制是怎么实现的呢? 借助于varargin。

相关:varargout、nargin

下面我们来看一个简单的例子,(本例子参考了matlab中varargin文档)

function retvar = vartest(varargin)

optargin = size(varargin, 2); % number of inputs.

ndims(varargin)

varargin

stdargin =nargin- optargin; % 'nargin' in matlab means number of input arguments.

fprintf('Number of inputs: %d\n',nargin);

fprintf('Inputs from individual arguments: %d\n', stdargin)

for k = 1:size(varargin, 2)

fprintf('%d: %d\n', k, varargin{k});

end

end

这里定义了一个函数, 利用了可变参数列表。然后我们这样调用这个函数:

>> vartest(1, 2, 3)

ans = 2

varargin =

[1] [2] [3]

Number of inputs: 3

Inputs from individual arguments: 0

1: 1

2: 2

3: 3

我们看到, 这里varargin是一个1*3的二维矩阵, 这个矩阵即我们调用这个函数时传入的参数列表。

通过size(varargin, 2)获得的varargin第二维的尺寸(即varargin的列数)就是我们传入的参数个数。

stdargin =nargin- optargin;这一句是获取可变参数列表从第几个参数开始的。 其中,nargin也是matlab中的, 不能拼错了, nargin的

值即传入的所有参数个数。

也许你会问, 咦? 这不就是size(varargin, 2)吗?

对于本例,的确这样子。

但是有的函数,参数列表是这样的:

function vartest_2(arg1, argb, varargin)

optargin = size(varargin, 2); % number of inputs.

stdargin =nargin- optargin; % 'nargin' in matlab means number of input arguments.

fprintf('Number of inputs: %d\n',nargin);

fprintf('Inputs from individual arguments: %d\n', stdargin)

for k = 1:size(varargin, 2)

fprintf('%d: %d\n', k, varargin{k});

end

end

这次我们调用:

>> vartest_2(1, 2, 3)

Number of inputs: 3

Inputs from individual arguments: 2

1: 3

你会看到, 由于vartest_2的第一二个参数不是可变参数列表的一部分, 可变参数列表从第三个参数开始。因此

nargin等于3, 而size(varargin, 2) 等于1。

matlab 变长参数,变长参数函数的概念相关推荐

  1. python函数用法详解2(变量的作用域(全局变量、局部变量)、共享全局变量、函数返回值、函数的参数(位置参数、关键字参数、默认参数、不定长参数)、拆包、交换变量值、引用、可变和不可变类型)

    1. 变量作⽤域         变量作⽤域指的是变量⽣效的范围,主要分为两类:局部变量和全局变量. 局部变量         定义在函数体内部的变量,即只在函数体内部⽣效. def testA(): ...

  2. java变长参数_Java可变长度参数

    术语"varargs"是"variable-length arguments"(可变长度参数)的缩写. 可变长度参数声明一个接受可变数量的参数(或参数)的方法或 ...

  3. python不定长参数怎么相加_python函数不定长参数使用方法解析

    这篇文章主要介绍了python函数不定长参数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 pathon中的函数可以使用不定长参数,可以 ...

  4. python笔记之函数参数(缺省参数,命名参数,不定长参数)

    缺省参数 函数中定义带有初始值的形参 参数调用时,缺省参数可传,可不传 缺省参数一定在参数列表的最后面 缺省参数的数量没有限制 def x_y_sum(x,y=20): #缺省参数要在参数列表的最后p ...

  5. PYTHON 笔记:函数的参数(关键字参数,默认参数,可变长参数,可变长的关键字参数)

    一般的函数参数是位置参数,位置不对会出问题 1.关键字参数 使用参数名提供的参数就是关键字参数,有了关键字参数,顺序就不会有影响. >>>def printGrade(name,ID ...

  6. python关键字参数必须位于位置参数之前_python函数中的参数(关键字参数,默认参数,位置参数,不定长参数)...

    默认参数:定义函数的时候给定变量一个默认值. def num(age=1): 位置参数:调用函数的时候根据定义函数时的形参位置和实参位置进行引用. 关键字参数:如果定义的函数中含有关键字参数,调用函数 ...

  7. bouc wen matlab,基于Matlab磁流变阻尼器Bouc-Wen模型的参数识别

    2018年 3月 第 46卷 第 5期 机床与液压 MACHINE TO0L& HYDRAULICS Mat.2018 Vo1.46 No.5 DOI:10.3969/j.issn.1001- ...

  8. python中可选参数和可变参数_Python函数中的可变长参数详解

    一.Python函数中的参数 1.使用python的函数时,有参数类别,比如位置参数.关键字参数.可变长参数 2.位置参数.关键字参数很好理解,关键是可变长参数经常能见到,但是一直没有搞懂是什么意思 ...

  9. python可变参数和关键字参数位置_python中函数的默认参数和可变长参数如何排列?...

    参数在python中总是通过赋值进行传递的.在默认情况下,参数是通过其位置进行匹配的,从左到右,而且必须精确的传递和函数头部参数名一样多的参数. 这种默认的传递方式很简单 def f(a,b,c): ...

最新文章

  1. 你想要的自动驾驶汽车,为何迟迟不能出现?
  2. 安装带有调试信息的C库
  3. Python Xml类
  4. 这些单词你都念对了吗?顺便推荐三份程序员专属英语教程!
  5. atlas 又多了几个新控件
  6. shell 中的expect 用法
  7. 沪江快速手机打字软件下载
  8. 全自动高清录播服务器,常态化高清录播服务器 高清全自动录播系统
  9. Win10自带的邮件email上如何登录qq邮箱
  10. colab读取Google Drive
  11. excel表格打印每页都有表头_13个关于Excel表格的打印技巧
  12. 【全局盘点】华为云政企全栈技术创新能力图谱
  13. 带刺玫瑰特别美?OLED屏幕画面美但眼睛会累
  14. 微信小程序------登录
  15. PDF在线转Word文本软件
  16. 算法设计与分析: 5-10 排列宝石问题
  17. 【mysql】You must reset your password using ALTER USER statement before executing this statement报错处理
  18. 计算机电源插座安装,电源安装图解
  19. 嵌入式(stm32)学习之路---无源蜂呜器
  20. Oracle VM VirtualBox虚拟机上网配置-实现多台虚拟机局域网独立ip,可连接外网

热门文章

  1. 【转载】Linux截图工具
  2. 转:多线程环境下调用 HttpWebRequest 并发连接限制
  3. 20162303 2016-2017-2 《程序设计与数据结构》第五周学习总结
  4. The Security Learning
  5. python r语言 结合 部署_(转)python中调用R语言通过rpy2 进行交互安装配置详解...
  6. 在qt中用c语言数组,在QT函数中返回一个数组/把一个数组传参给函数
  7. soapui返回值类型都有哪些_货架的类型都有哪些呢
  8. Mysql表结构升级_mysql表结构升级时根据字段是否存在执行相应操作
  9. mac上php环境_在Mac系统下配置PHP运行环境
  10. 【mathematical statistics】5 distributional testing