C语言之变参函数

C语言之变参函数

目录

1. 变参函数定义

  • 函数可以接受不同类型的参数,也可以接受不同个数的参数

2. printf函数

root@python:~# man 3 printf

SYNOPSIS
       #include <stdio.h>

       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int dprintf(int fd, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const char *format, ...);

       #include <stdarg.h>

       int vprintf(const char *format, va_list ap);
       int vfprintf(FILE *stream, const char *format, va_list ap);
       int vdprintf(int fd, const char *format, va_list ap);
       int vsprintf(char *str, const char *format, va_list ap);
       int vsnprintf(char *str, size_t size, const char *format, va_list ap);

int printf(const char *format, …);
printf()函数第一个参数是一个字符指针,第二个参数是三个圆点,这说明printf()除了第一个参数外,从第二个参数开始其个数和类型是不确定的。C语言的变参是“阉割版”的变参,因为它必须有一个固定的参数,并且必须是第一个。【其原因是Linux环境下C程序的函数被执行时,其形参表中的各参数是从右往左依次在栈中被分配】
例如:

void sumup()
{
   printf("%d%c%lf",100,'b',3.45);
}

其在系统中的内存布局如下所示:
step1

我们可以根据第一个已知的参数所提供的线索,来回溯剩下的未知的参数。

通过下面的代码详细了解C程序变参内部实现细节:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#define MAXLINE 80

double sumup(const char *format, ...)
{
  va_list arg_ptr; //定义一个用来回溯堆栈变参的指针arg_ptr
  va_start(arg_ptr,format); //使arg_ptr指向固定参之后第一个变参

  int argnum = 0,i;
  char arg[MAXLINE];
  bzero(arg,MAXLINE);

  //提取自定义的格式控制符,并存放在arg[]中
  for(i = 0;format[i]!='\0';i++)
  {
    if(format[i]=='#')
    {
     arg[argnum] = format[++i];
     argnum++;
    }
  }
  double sum = 0;
  int arg_int;
  double arg_double;

 for(i = 0;arg[i]!= '\0';i++)
 {
   switch(arg[i])
   {
     case 'i':
     case 'c':
        arg_int = va_arg(arg_ptr,int);
        sum += arg_int;
        break;
    case 'f':
        arg_double = va_arg(arg_ptr,double);
        sum += arg_double;
        break;
    default:
        printf("format error...\n");
        exit(1);

   }
 }
 va_end(arg_ptr);
 /*使用va_end()来确保变参取完之后arg_ptr恢复到不可用的状态,避免下次不小心再次对arg_ptr进行引用导致系统段错误(Sagmentation Fault)*/
 return sum;
}


int main(void)
{
 int i=1;
 char c = 'B';
 double f = 0.618;

 /*模仿printf() 约定#i代表int #f代表double #c代表char*/
 printf("summary:%f\n",sumup("#i#c#f",i,c,f));

 return 0;
}

root@python:/home/zbb# gcc sum.c
root@python:/home/zbb# ls
a.out sum.c
root@python:/home/zbb# ./a.out
summary:67.618000

3. vsprintf函数

int vsprintf (char str, const char * format, va_list arg );;*
将格式化数据从变量参数列表写入字符串,如果在printf上使用格式,但使用由arg标识的变量参数列表中的元素(而不是附加函数参数),并将结果内容作为C字符串存储在由s指向的缓冲区中。

在内部,该函数从arg标识的列表中检索参数,就好像va_arg被使用了一样,因此arg的状态可能会被调用改变。

在任何情况下,arg应该在调用之前的某个时间点由va_start初始化,并且在调用之后的某个时间点预计会由va_end释放。

参数列表

参数备注
str指向存储结果C字符串的缓冲区的指针。缓冲区应该足够大以包含结果字符串
format包含格式字符串的C字符串,其格式字符串与printf中的格式相同(请参阅printf以了解详细信息)
arg标识用va_start初始化的可变参数列表的值,va_list是一个在中定义的特殊类型
返回值成功后,返回写入的字符总数。失败时,返回负数。
/* vsprintf example */
#include <stdio.h>
#include <stdarg.h>

void PrintFError ( const char * format, ... )
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsprintf (buffer,format, args);
  perror (buffer);
  va_end (args);
}

int main ()
{
  FILE * pFile;
  char szFileName[]="myfile.txt";

  pFile = fopen (szFileName,"r");
  if (pFile == NULL)
    PrintFError ("Error opening '%s'",szFileName);
  else
  {
    // file successfully open
    fclose (pFile);
  }
  return 0;
}

In this example, if the file myfile.txt does not exist, perror is called to show an error message similar to:

root@python:/home/zbb# gcc test.c -o test
root@python:/home/zbb# ls
a.out  sum.c  test  test.c
root@python:/home/zbb# ./test
Error opening 'myfile.txt': No such file or directory

4. Bitcoin sources Satoshi 0.1.5/​util.cpp

http://btc.yt/lxr/satoshi/source/util.cpp?v=0.1.5

// Safer snprintf

//  - prints up to limit-1 characters

//  - output string is always null terminated even if limit reached

//  - return value is the number of characters actually printed

 int my_snprintf(char* buffer, size_t limit, const char* format, ...)
 {
     if (limit == 0)
         return 0;
     va_list arg_ptr;
     va_start(arg_ptr, format);
     int ret = _vsnprintf(buffer, limit, format, arg_ptr);
     va_end(arg_ptr);
     if (ret < 0 || ret >= limit)
     {
         ret = limit - 1;
         buffer[limit-1] = 0;
     }
     return ret;
 }


 string strprintf(const char* format, ...)
 {
     char buffer[50000];
     char* p = buffer;
     int limit = sizeof(buffer);
     int ret;
     loop
     {
         va_list arg_ptr;
         va_start(arg_ptr, format);
         ret = _vsnprintf(p, limit, format, arg_ptr);
         va_end(arg_ptr);
         if (ret >= 0 && ret < limit)
             break;
         if (p != buffer)
             delete p;
         limit *= 2;
         p = new char[limit];
         if (p == NULL)
             throw std::bad_alloc();
     }
 #ifdef _MSC_VER
     // msvc optimisation

     if (p == buffer)
         return string(p, p+ret);
 #endif
     string str(p, p+ret);
     if (p != buffer)
         delete p;
     return str;
 }
 /*
  string();
  string( size_type length, char ch );
  string( const char *str );
  string( const char *str, size_type length );
  string( string &str, size_type index, size_type length );
  string( input_iterator start, input_iterator end );
    字符串的构造函数创建一个新字符串,包括:

    以length为长度的ch的拷贝(即length个ch)
    以str为初值 (长度任意),
    以index为索引开始的子串,长度为length, 或者
    以从start到end的元素为初值.
  */

 bool error(const char* format, ...)
 {
     char buffer[50000];
     int limit = sizeof(buffer);
     va_list arg_ptr;
     va_start(arg_ptr, format);
     int ret = _vsnprintf(buffer, limit, format, arg_ptr);
     va_end(arg_ptr);
    if (ret < 0 || ret >= limit)
     {
         ret = limit - 1;
         buffer[limit-1] = 0;
     }
     printf("ERROR: %s\n", buffer);
     return false;
 }


 void PrintException(std::exception* pex, const char* pszThread)
 {
     char pszModule[260];
     pszModule[0] = '\0';
    GetModuleFileName(NULL, pszModule, sizeof(pszModule));
     _strlwr(pszModule);
     char pszMessage[1000];
     if (pex)
         snprintf(pszMessage, sizeof(pszMessage),
             "EXCEPTION: %s       \n%s       \n%s in %s       \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
     else
         snprintf(pszMessage, sizeof(pszMessage),
             "UNKNOWN EXCEPTION       \n%s in %s       \n", pszModule, pszThread);
     printf("\n\n************************\n%s", pszMessage);
     if (wxTheApp)
         wxMessageBox(pszMessage, "Error", wxOK | wxICON_ERROR);
     throw;
     //DebugBreak();

 }
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Erice_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值