文档章节

函数可变参数

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:46
字数 877
阅读 9
收藏 0

程序debug过程中经常会打印调试信息,如果我们要在程序运行过程当中看到调试信心,就得把运行的状态信息输出到文件。于是,我写了下面两个函数来实现可变参数的打印调试信息到文件。

 

#include "stdio.h"
#include "stdarg.h"

 

int print_string_to_file(char file_name[64], ...)
{
 va_list ap;
 char *p;
 FILE* fp;

 fp = fopen(file_name, "ab");
 if (fp == NULL)
 {
  return -1;
 }


 va_start(ap, file_name);
 p = va_arg(ap, char *);

 while (p != NULL)
 {
  fprintf(fp, "%s/n", p);
  p = va_arg(ap, char*);
 }
 va_end(ap);
 fclose(fp);
 return 0;
}

 

 

static char szBuffer[8*1024];

 

int print_data_to_file (char file_name[64], const char * szFormat, ...)
{
 int     iReturn ;
 va_list pArgs ;
 FILE* fp;

 fp = fopen(file_name, "ab");
 if (fp == NULL)
 {
  return -1;
 }

 va_start (pArgs, szFormat) ;
 iReturn = vsprintf (szBuffer, szFormat, pArgs) ;
 fprintf(fp, "%s",szBuffer);
 va_end (pArgs) ;
 fclose(fp);
 return iReturn ;
}

 

其中,第一个函数只能打印字符串,并且调用的时候最后一个参数必须传入一个NULL指针,从里面的代码也可以看到可变参数的实现技巧。

而第二个函数可以按照格式化的信息打印,类似于printf的format。简单的调用代码如下:

 

 

 

int main(int argc, char* argv[])
{

 float f = 7.0f;

 

 print_string_to_file("info.txt", "123","string", NULL);


 print_data_to_file("info2.txt","str = %s, float num = %f/n", "string", f);


 return 0;

 

 

 可变参数的函数实现实际上与函数参数传递的堆栈结构有关,在x86平台,一般的c编译器的参数传递默认按照从右到左的,即函数中的最右边的参数最先入栈的顺序。这个实际上与调用约定有关。对于函数

 

void fun1(char a, int b, double c, short d) ;

 

 

如果知道了参数a的地址,则要取后续参数的值则可以通过a的地址计算a后面参数的地址,然后取对应的值,而后面参数的个数可以直接由变量a指定,当然也可以像printf一样根据第一个参数中的%模式个数来决定后续参数的个数和类型。如果参数的个数由第一个参数a直接决定,则后续参数的类型如果没有变化并且是已知的,则我们可以这样来取后续参数, 假定后续参数的类型都是double;

 

 

void fun1(int num, ...)
{
    double *p = (double *)((&num)+1);
    double Param1 = *p;
    double Param2 = *(p+1);
    ...
    double Paramn  *(p+num);
}

 

如果后续参数的类型是变化而且是未知的,则必须通过一个参数中设定模式来匹配后续参数的个数和类型,例如printf就是这样.

 

综上,我实现了一个求解n个整数的最大值的可变参数函数。

 

 

int Max(int n, ...)
{
    int *p = &n + 1;
    int ret = *p;
    for (int i=0; i<n; i++)
    {
        if (ret < *(p + i))
            ret = *(p + i);
    }
    return ret;
}


调用方式如下:

 

 

int main(int argc, char* argv[])
{

 int m3 = Max_num(3, 45, 12, 56);
 int m1 = Max_num(1, 3);
 int m2 = Max_num(2, 23, 45);

 int first = 34, second = 45, third=5;
 int m5 = Max_num(5, first, second, third, 100, 4);

}

 

 

 结论

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

 

本文转载自:http://www.cnblogs.com/celerychen/archive/2011/06/30/3588218.html

共有 人打赏支持
abcijkxyz
粉丝 63
博文 6196
码字总数 1876
作品 0
深圳
项目经理
私信 提问

暂无文章

EOS官方钱包keosd

EOS官方钱包的名称是keosd,它负责管理你的私钥,并且帮你进行交易的签名。 不过不幸的是,keosd钱包对普通用户并不友好,它是一个命令行程序,目前还没有像以太坊的mist那样的图形化界面,而...

汇智网教程
今天
20
0
ArrayList的实现原理以及实现线程安全

一、ArrayList概述 ArrayList是基于数组实现的,是一个动态的数字,可以自动扩容。 ArrayList不是线程安全的,效率比较高,只能用于单线程的环境中,在多线程环境中可以使用Collections.syn...

一看就喷亏的小猿
今天
20
0
Netty 备录 (一)

入职新公司不久,修修补补1个月的bug,来了点实战性的技术---基于netty即时通信 还好之前对socket有所使用及了解,入手netty应该不是很难吧,好吧,的确有点难,刚看这玩意的时候,可能都不知道哪里...

_大侠__
昨天
30
0
Django简单介绍和用户访问流程

Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。 Django是一个开放源代码的Web应用框架,由Python写成。 Django遵守BSD版权,初...

枫叶云
昨天
36
0
Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)

应用场景 之前我们已经通过《Spring Cloud Stream消费失败后的处理策略(一):自动重试》一文介绍了Spring Cloud Stream默认的消息重试功能。本文将介绍RabbitMQ的binder提供的另外一种重试...

程序猿DD
昨天
21
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部