实现printf以及Sprintf源码
printf函数的全称为格式化输出函数。之所以叫做格式化出,是因为这个函数提供有多种格式化输出方式,可以向标准输出中输出某种格式的内容。本文目标是详细的讲述printf函数的用法,写法,原理。
int printf(const char *format, ...); //...在C中是合法的
E. printf("%s,666,%s",a ,b);
"%s,666,%s"为format. printf是一个带有不定参量的函数
为了进一步理解不定参量,我们引入stdarg头文件,举个例子如何实现不定参量:
void printargs(int arg1, ...)
{
va_list ap; //定义参数列表结构体
int i;
va_start(ap, arg1); //以最后一个已知参数作为第二参量
for (i = arg1; i >= 0; i = va_arg(ap, int)) // 解析得到一个int型的参量
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, -1, 48, -1);
printargs(84, 51, -1, 3);
printargs(-1);
printargs(1, -1);
return 0;
}
通过上述方式我们能够学会如何通过stdarg实现不定参量作为参数列表的函数。
那么下一步讲解printf的具体实现,首先printf需要完成能够接收%d、%s、%p等输出格式控制符,我们进行了如下的实现:
int vsprintf(char *out, const char *fmt, va_list ap) {
char* buf_p = out;
static char str_temp[64];
while (*fmt != '\0') {
if (*fmt != '%') {
*buf_p++ = *fmt++;
}
else {
switch (*(++fmt)) {
case 'c':
char ch = va_arg(ap, int);
*buf_p++ = ch;
break;
case 's':
char* str = va_arg(ap, char*);
size_t len_s = strlen(str);
strcpy(buf_p, str);
buf_p += len_s;
break;
case 'd':
int num_d = va_arg(ap, int);
char* num_d2str = itoa(num_d, (char*)&str_temp, 10);
strcpy(buf_p, num_d2str);
size_t len_d = strlen(num_d2str);
buf_p += len_d;
break;
case 'p':
int num_p = va_arg(ap, int);
char* num_p2str = itoa(num_p, (char*)&str_temp, 16);
strcpy(buf_p, num_p2str);
size_t len_p = strlen(num_p2str);
buf_p += len_p;
break;
default:
break;
}
fmt++;
}
}
*buf_p++ = '\0';
return buf_p - out;
}
阅读上述代码,它能够实现对一串字符串的转义,能够将所有的格式控制符进行处理,并且将之最后的结果放入到字符串中。
那么很容易实现下列printf的函数:
int printf(const char *fmt, ...) {
va_list args;
int ret;
va_start(args, fmt);
ret = vsprintf(printf_buf, fmt, args);
va_end(args);
puts(printf_buf);
return ret;
}
sprintf函数实现
sprintf只需要进行简单的改动即可
int sprintf(char *out, const char *fmt, ...) {
va_list args;
int i;
va_start(args, fmt);
i = vsprintf(out, fmt, args);
va_end(args);
return i;
}