C语言中可变参数的使用
在C语言程序编写中我们使用最多的函数一定包括printf以及很多类似的变形体。这个函数包含在C库函数中,定义为 int printf( const char* format, ...);
除了一个格式化字符串之外还可以输入多个可变参量,如:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
格式化字符串的判断本章暂且不论,下面分析一下可变参数的实现细节。
一、简单实例
int simple(int num,...)
{
int i, result=;
va_list vl; //va_list指针,用于va_start取可变参数,为char*
va_start(vl,num); //取得可变参数列表中的第一个值
printf("num:%d, vl:%d\n",num,*vl);
for (i = ; i < (num-); i++)//这里num表示可变参数列表中有多少个参数
{
result = va_arg(vl, int);//这里把vl往后跳过4个字节(sizeof(int)大小)指向下一个参数,返回的是当前参数(而非下一个参数)
printf("in for result:%d, *vl:%d\n", result, *vl);//这里打印下,可以看出,vl总是指向result后面的那个参数
}
va_end(vl);//结束标志
return result; }
int main(int argc, char **argv)
{
int num = argc;
int i = ;
simple(,,,,,);
return ;
}
运行结果如下:
num:, vl:
in for result:, *vl:
in for result:, *vl:
in for result:, *vl:
in for result:, *vl:5
二、相关API
可变参数列表的实现是由几个宏组成的,在文件include/stdarg.h中:
va_list 定义某个变量,内核中的定义:
typedef char *va_list;//字符指针类型
va_start(ap, type) 开始获取可变参数列表中的第一个参数(...里面的第一个),也就是跳过第一个参数(即num):
#ifndef __sparc__
#define va_start(AP, LASTARG) \
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))//ap指向下一个参数,lastarg不变
#else
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), \
AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) //跳过下第一个参数,指向第二个参数内存地址
#endif //对type向上取整 取int的整 4,然后乘上int整型4的倍数
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - ) / sizeof (int)) * sizeof (int))
va_arg(args, int) 循环获取到可变参数列表中的参数,args指向下一个参数地址,返回的则是当前参数地址
// first=va_arg(args,int)
#define va_arg(AP, TYPE) \//ap指向下一个类型的参数
(AP += __va_rounded_size (TYPE), \//返回ap - sizeof(type)参数,即前一个参数
*((TYPE *) (AP - __va_rounded_size (TYPE)))) //对type向上取整 取int的整 4,然后乘上int整型4的倍数
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - ) / sizeof (int)) * sizeof (int))
最后一个va_end(ap)结束标志,可能只是在程序中作为一个可变参数列表的结束标志而已(stdarg.h里面只是仅仅定义了下,没有实现的代码部分)。
三、注意事项
因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利
如将simple可变参数该成char型指针,若存在空指针在会产生coredump
void simple(int i, ...)
{
va_list arg_ptr;
char *s=NULL; va_start(arg_ptr, i);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s\n", i, s);
return;
}
可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现core dump(Unix) 或者页面非法的错误(window平台).但也有可能不出错,但错误却是难以发现,不利于我们写出高质量的程序。
C语言中可变参数的使用的更多相关文章
- C语言中可变参数的函数(三个点,“...”)
C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end ...
- C语言中可变参数函数实现原理
C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...
- C语言中可变参数的用法
原文地址: http://blog.csdn.net/wooin/archive/2006/04/29/697106.aspx 我们在C语言编程中会遇到一些参数个数可变的函数,例如printf() ...
- C语言中可变参数的原理——printf()函数
函数原型: int printf(const char *format[,argument]...) 返 回 值: 成功则返回实际输出的字符数,失败返回-1. 函数说明: 使用过C语言的人所再熟悉不过 ...
- c 中可变参数的实现
我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: 例一: int printf( const char* format, ... ...
- c语言中函数参数入栈的顺序是什么?为什么
看到面试题C语言中函数参数的入栈顺序如何? 自己不知道,边上网找资料.下面是详细解释 #include <stdio.h> void foo(int x, int y, int z){ ...
- [11 Go语言基础-可变参数函数]
[11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...
- C语言的可变参数在Linux(Ubuntu)与Windows下注意点
基本上C语言的可变参数原理在不同平台和不同编译器下基本类似(通过函数入栈,从右向左,从高位到低位地址),不过部分实现会有所不同:在使用中需要注意的是: va_list 为char 类型指针,部分调用如 ...
- Java 中可变参数
可变参数 Java 中可变参数 现在需要编写一个求和的功能,但是不知道有几个参数,在调用的时候才知道有几个参数,请问这如何实现呢? Java 给我们提供了一个 JDK 1.5 的新特性---可变参数 ...
随机推荐
- SpringBoot | 第三十七章:集成Jasypt实现配置项加密
前言 近期在进行项目安全方面评审时,质量管理部门有提出需要对配置文件中的敏高文件进行加密处理,避免了信息泄露问题.想想前段时间某公司上传github时,把相应的生产数据库明文密码也一并上传了,导致了相 ...
- java实现定时任务(Quartz)
java中实现定时任务执行某一业务.具体操作如下: 1.定义初始化任务 2.任务业务操作 3.定义初始化方法 4.在web.xml中注册启动 5.定义具体执行时间 6.quartz定时任务时间设置 q ...
- 【练习笔记】spring 配置Schedule
spring项目一些简单的定时任务可以通过@Schedule注解来实现,具体配置如下 在applicationContext.xml文件中增加配置 1.引入task约束 xmlns:task=&quo ...
- 数据库(JDBC、DBUtils)
JDBC(Java DataBase Connection) 今日内容介绍 u SQL语句查询 u JDBC 第1章 JDBC 1.1 JDBC概述 JDBC(Java Data Base Conn ...
- 子元素的margin-top会影响父元素
---恢复内容开始--- 之前在写项目的时候,发现原本想让父子元素之间加点边距,却让父元素产生了margin-top,于是百度之后发现了原因. 在css2.1盒模型中 In this specific ...
- JSP的使用
JSP全称为(Java server page),之所以出现JSP,最主要的目的是抽离出Servlet中输出HTML的部分,由于之前响应客户端的时候,是直接在Servlet中利用response.ge ...
- [AngularJS] $location 服务简介
参考博客: https://www.cnblogs.com/gaoruixin/p/6070502.html 简介 $location服务解析在浏览器地址栏中的URL(基于window.locati ...
- JFinal常量配置学习笔记
在继承 JFinalConfig 类时,需要 实现 /** * Config constant */ public abstract void configConstant(Constants me) ...
- LeetCode Nim Game (简单nim)
题意: 有一堆石子,里面有n个石头,每次可以从中取出1~3个,两人轮流取,最后一个石子被谁取走即为赢家.你先取,问最后谁赢? 思路: n%4>0则先手赢,因为每次总是可以给对方留4个石子的倍数, ...
- spa 小程序的研发随笔 (1) --- 前言
半年前跳槽, 新公司主要研发倾向于小程序的开发.由于之前并没有接触小程序,所以经过半年的实际开发,才敢来做一点笔记. 小程序提供很多组件给开发者使用,但是,实际使用中还是会有很多的问题. 小程序的组件 ...