vsprintf函数+源码_vsprintf函数实现

hacker|
113

文章目录:

vsprintf()函数 在哪个头文件里??

stdarg.h

stdarg.h是C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数。C++的cstdarg头文件中也提供这样的功能;虽然与C的头文件是兼容的,但是也有冲突存在。

可变参数函数(Variadic functions)是stdarg.h内容典型的应用,虽然也可以使用在其他由可变参数函数调用的函数(例如,vprintf)。

如何使用vsprintf,vsnprintf等函数

format函数实现

std::string format(const char *fmt, ...) {

va_list args, args1;

va_start(args, fmt);

va_copy(args1, args);

string res(1 + vsnprintf(nullptr, 0, fmt, args1), 0);

va_end(args1);

vsnprintf(res[0], res.size(), fmt, args);

va_end(args);

return res;

}

代码讲解

std::string format(const char *fmt, ...) {

// 定义两个va_list 类型的变量,这种变量可以用来处理变长参数:...

va_list args, args1;

// 初始化args

va_start(args, fmt);

// args1 是 args 的一个拷贝

va_copy(args1, args);

// 使用nullptr和0作为前两个参数来获取格式化这个变长参数列表所需要的字符串长度

// 使用 string(size_t n, char c) 构造函数,构造一个长度为n的字符串,内容为n个c的拷贝

string res(1 + vsnprintf(nullptr, 0, fmt, args1), 0);

// args1 任务完成,将其关闭,清理。

va_end(args1);

// 使用args来格式化要返回的字符串res, 指定长度size

vsnprintf(res[0], res.size(), fmt, args);

// args 任务完成,关闭,清理

va_end(args);

return res;

}

与vsnprintf族函数类似的,还有snprintf族函数, 用法大同小异。v开头的接受的参数类型是va_list, snprintf等则直接接收参数..

printf函数的实现方式

其实printf和getchar()类似,它们都是一个”外壳“,真正实现功能的不是它本身,而是通过调用别的函数。

getchar() is equivalent to getc(stdin).

printf有一家子print函数

printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output conversion

它们的声明在不同的header file里面

#include stdio.h

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

int fprintf(FILE *stream, 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 vsprintf(char *str, const char *format, va_list ap);

int vsnprintf(char *str, size_t size, const char *format, va_list ap);

snprintf(), vsnprintf():

这两个函数是C99新加的,编译的时候 注意 -std=c99

实现之前还是“复习”一下printf比较好,就当是铺垫

有意思的是printf的declaration。

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

返回值是int,第一个参数是const字符串指针,第二个参数是个...

先看看返回值int有哪些情况

Return value

Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).

嗯哼。。。返回的是成功打印的字符的个数,这里不包括NULL

demo:

#includestdio.h

int main()

{

int counter = 0;

counter = printf("hello world! %d\n",10);

printf("counter is %d\n",counter);

return 0;

}

jasonleaster@ubuntu:~$ ./a.out

hello world! 10

counter is 16

接着,第一个参数是一个指针,指向const字符串

Format of the format string

The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of

zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifications,

each of which results in fetching zero or more subsequent arguments. Each conversion specification is introduced by the character %,

and ends with a conversion specifier. In between there may be (in this order) zero or more flags, an optional minimum field width,

an optional precision and an optional length modifier.

很少人会用下面这种用法

printf("%*d",10,5);

我第一次遇到的时候,可以说是“惊愕”,究竟会打印什么东西。折腾了好久,最后搞定了。总结在这里

Format of the format string

The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments. Each conversion specification is introduced by the character %, and ends with a conversion specifier. In between there may be (in this order) zero or more flags, an optional minimum field width, an optional precision and an optional length modifier.

The arguments must correspond properly (after type promotion) with the conversion specifier. By default, the arguments are used in the order given, where each '*' and each conversion specifier asks for the next argument (and it is an error if insufficiently many arguments are given). One can also specify explicitly which argument is taken, at each place where an argument is required, by writing "%m$" instead of '%' and "*m$" instead of '*', where the decimal integer m denotes the position in the argument list of the

desired argument, indexed starting from 1. Thus,

printf("%*d", width, num);

and

printf("%2$*1$d", width, num);

are equivalent. The second style allows repeated references to the same argument. The C99 standard does not include the style using '$', which comes from the Single UNIX Specification. If the style using '$' is used, it must be used throughout for all conversions taking an argument and all width and precision arguments, but it may be mixed with "%%" formats which do not consume an argument.

There may be no gaps in the numbers of arguments specified using '$'; for example, if arguments 1 and 3 are specified, argument 2

must also be specified somewhere in the format string.

第三个参数 ...

嗯,这家伙有点屌,叫做变长参数。把这个搞定,C总会有点长进的

这个stdarg.h 我在现在的GCC和现在的linux 3.0版本的内核里面找了好久,都木有,估计是封装到被的地方了。。。。

__builtin_va_start(v,l) 线索就死在这个地方。。。之后就找不到__builtin_va_start的定义了

还是看早起内核的实现吧

0.12内核里面的stdarg.h

#ifndef _STDARG_H

#define _STDARG_H

typedef char *va_list;

/* Amount of space required in an argument list for an arg of type TYPE.

TYPE may alternatively be an expression whose type is used. */

#define __va_rounded_size(TYPE) \

(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

#ifndef __sparc__

#define va_start(AP, LASTARG) \

(AP = ((char *) (LASTARG) + __va_rounded_size (LASTARG)))

#else

#define va_start(AP, LASTARG) \

(__builtin_saveregs (), \

AP = ((char *) (LASTARG) + __va_rounded_size (LASTARG)))

#endif

void va_end (va_list); /* Defined in gnulib */

#define va_end(AP)

#define va_arg(AP, TYPE) \

(AP += __va_rounded_size (TYPE), \

*((TYPE *) (AP - __va_rounded_size (TYPE))))

#endif /* _STDARG_H */

va_list 是一个指向字符串的指针

分析上面的宏定义#define __va_rounded_size(TYPE) \

(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

这个用来得到TYPE元素类型的字节大小,若不足4字节(例如short 和char),那么认为这个元素的大小为4字节,简单的说就是检测元素的大小,不足4字节的当作4字节看待。。。#define va_start(AP, LASTARG) \

(AP = ((char *) (LASTARG) + __va_rounded_size (LASTARG)))

AP一般都是va_list,LASTARG则是 指向参数变长函数的格式化字符串的指针.

va_start的作用就很明显了。取得变长参数列表的第一个参数的地址。

va_end 则是把指针va_list 置0 (通过google知道的,这个va_end真没找到定义,代码里面就一句#define 我无能为力啊。。。)

不过知道用va_start 和va_end 就OK啦

下面先来个变长参数的demo

/*****************************************************************************

code writer : EOF

code date : 2014.04.26

e-mail:jasonleaster@gmail.com

code purpose:

just a demo for varible parameter function.

usage: va_sum(a number,anohter number...,0);

va_sum(1,2,3,4,5,0); return 15

******************************************************************************/

#include stdarg.h

#include stdio.h

int va_sum(int* a,...);

int main()

{

int number = 1;

int foo = 0;

foo = va_sum(number,2,3,4,5,0);

return 0;

}

int va_sum(int* a,...)

{

int counter = 0;

int element = 0;

va_list arg;

va_start(arg,a);

while((element = va_arg(arg,int)) != 0)

{

counter += element;

}

va_end(arg);

return counter;

}

#define va_arg(AP, TYPE) \

(AP += __va_rounded_size (TYPE), \

*((TYPE *) (AP - __va_rounded_size (TYPE))))

C语言内部函数 printf() 具体是怎么写的

printf 是可变参数的函数,功能非常强大

typedef char *va_list;

#define _AUPBND (sizeof (acpi_native_int) - 1)

#define _ADNBND (sizeof (acpi_native_int) - 1)

#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) (~(bnd)))

#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

#define va_end(ap) (void) 0

#define va_start(ap, A) (void) ((ap) = (((char *) (A)) + (_bnd (A,_AUPBND))))

int printf(char *fmt, ...)

{

va_list args;

int n;

va_start(args, fmt);

n = vsprintf(sprint_buf, fmt, args);

va_end(args);

write(stdout, sprint_buf, n);

return n;

}

static inline long write(int fd, const char *buf, off_t count)

{

return sys_write(fd, buf, count);

}

通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,

然后再调用vsprintf

最后实际上是通过调用sys_write输出到标准输出设备,就是显示器上来实现的

sys_write(unsigned int fd, const char * buf, size_t count);

3条大神的评论

  • avatar
    访客 2022-07-14 上午 05:10:29

    characters (not %), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero

  • avatar
    访客 2022-07-14 上午 10:37:08

    nd)) (~(bnd))) #define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) #define va_end(ap) (void) 0 #d

  • avatar
    访客 2022-07-14 下午 12:37:39

    STARG) \ (AP = ((char *) (LASTARG) + __va_rounded_size (LASTARG)))AP一般都是va_list,LASTARG则是 指向参数变长函数的格式化字符串的指针.va_start的作用就很明显了

发表评论