root@python:~# man 3 printf
SYNOPSIS
#include
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
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);
}
我们可以根据第一个已知的参数所提供的线索,来回溯剩下的未知的参数。
通过下面的代码详细了解C程序变参内部实现细节:
#include
#include
#include
#include
#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
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
#include
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
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();
}