文章目录
解释
自己实现该库函数是进行南大-计算机系统基础课程的要求. 以下有我进行学习的几次总结笔记.
源代码见https://github.com/xiao-tai/ics2021/blob/master/abstract-machine/klib/src/stdio.c
printf
用于向标准流中输出信息.
printf
只是一个调用va_start
, vprintf
, va_end
的包装, 底层核心函数是vsprintf
具体解释可以man 3 printf
查看(Linux下), 对于va_list
, 可以自行搜索资料了解.
printf
函数:
int printf(const char *fmt, ...) {
char buffer[1024];
va_list arg;
va_start(arg, fmt);
int done = vsprintf(buffer, fmt, arg); // 将格式化的内容(字符串)保存在buffer中
putstr(buffer);
va_end(arg);
return done;
}
同理, sprintf
函数:
int sprintf(char *out, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int res = vsprintf(out ,fmt, arg); // 将格式化的内容(字符串)赋值给out
va_end(arg);
return res;
}
上述函数都使用到了vsprintf
, 至于为什么要再套一个vsnprintf
, 是因为还有snprintf
要使用vsnprintf
.
int vsprintf(char *out, const char *fmt, va_list ap) {
return vsnprintf(out, -1, fmt, ap);
}
vsnprintf代码实现
代码还有待完善优化, 只是提供一种实现的思路.
static char NUM_CHAR[] = "0123456789ABCDEF"; // 为后面取余做铺垫
int vsnprintf(char *out, size_t n, const char *fmt, va_list ap) {
int len = 0;
char buf[128];
int buf_len = 0;
while(*fmt != '\0' && len < n){
switch(*fmt) {
case '%':
fmt++;
// 检查百分号之后的字符
switch(*fmt) {
case 'd':
int val = va_arg(ap, int); // 将该参数转为int型
if(val == 0) out[len++] = '0';
if(val < 0) {
out[len++] = '-';
val = 0 - val;
}
for(buf_len = 0, val; val /= 10, buf_len++)
buf[buf_len] = NUM_CHAR[val % 10]; //这里buf会是逆序的
for(int i = buf_len - 1; i >= 0; i--)
out[len++] = buf[i];
break;
case 'u':
uint32_t uval = va_arg(ap, uint32_t);
// 同%d, 只不过不用考虑负数
break;
case 'c':
char c = (char)va_arg(ap, int); //va_arg函数没有char这个参数
out[len++] = c;
break;
case 's':
char *s = va_arg(ap, char*);
for(int i = 0; s[i] != '\0'; i++)
out[len++] = s[i];
break;
case 'p':
out[len++] = '0'; out[len++] = 'x';
uint32_t address = va_arg(ap, uint32_t);
for(buf_len = 0; address; address /= 16, buf_len++)
buf[buf_len] = NUM_CHAR[address % 16];
for(int i = buf_len - 1; i >= 0; i--)
out[len++] = buf[i];
break;
}
break; // case % 的break.
case '\n':
out[len++] = '\n';
break;
default:
out[len++] = *fmt;
}
fmt++;
}
out[len] = '\0'; // 这句千万不能漏.
return len;
}