int myprintf(const char *format, ...) {
va_list ap;
int data;
char str[32] = {0};
char *p;
va_start(ap, format);
while (*format != '\0') {
if (*format == '%') {
format++;
switch (*format) {
case 'x': /* TODO: 十六進制 */ break;
case 'o': /* TODO: 八進制 */ break;
case '%': /* TODO: 輸出 % 本身 */ break;
case 's': p = va_arg(ap, char *); myputs(p); break;
case 'd': data = va_arg(ap, int); str[0] = '\0'; itoa(data, str); myputs(str); break;
case 'c': data = va_arg(ap, int); putchar(data); break;
}
} else {
putchar(*format);
}
format++;
}
va_end(ap);
}
case 'x':
data = va_arg(ap, int);
str[0] = '\0';
// itoa 不支援基數 16,自寫或用 sprintf
sprintf(str, "%x", data);
myputs(str);
break;
case 'o':
data = va_arg(ap, int);
str[0] = '\0';
sprintf(str, "%o", data);
myputs(str);
break;
case '%':
putchar('%');
break;
%x/%o 取 int。itoa,建議用 sprintf 或自寫 utoa。%% 輸出單個 %,故 case '%' 直接 putchar('%')。char str[32] 足夠容納 32 位整數的八/十六進制(最多 11 字元含結束符)。#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void myputs(const char *s) { while (*s) putchar(*s++); }
int myprintf(const char *format, ...) {
va_list ap;
int data;
char str[32];
va_start(ap, format);
while (*format) {
if (*format == '%') {
format++;
switch (*format) {
case 'x': case 'X':
data = va_arg(ap, int);
sprintf(str, "%x", data); myputs(str); break;
case 'o':
data = va_arg(ap, int);
sprintf(str, "%o", data); myputs(str); break;
case '%': putchar('%'); break;
case 's': myputs(va_arg(ap, char*)); break;
case 'd':
data = va_arg(ap, int);
sprintf(str, "%d", data); myputs(str); break;
case 'c': putchar(va_arg(ap, int)); break;
default: putchar(*format); break; // 未知格式原樣輸出
}
} else {
putchar(*format);
}
format++;
}
va_end(ap);
return 0;
}
int main() {
myprintf("[%c]hello[%d][%s]world\n", 'A', 123, "Jason");
myprintf("Hex: %x, Oct: %o, Percent: %%\n", 255, 255);
return 0;
}
[A]hello[123][Jason]world
Hex: ff, Oct: 377, Percent: %
理解 va_list 機制,手寫 printf 其實不難,關鍵在格式解析與參數取用同步。