From ead838bffd90947bf9fe0830026a4398f9cf8e5c Mon Sep 17 00:00:00 2001 From: fslongjin Date: Sun, 23 Jan 2022 23:17:52 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20=E5=AE=8C=E6=88=90=E4=BA=86printk?= =?UTF-8?q?=EF=BC=88=E6=9A=82=E4=B8=8D=E6=94=AF=E6=8C=81=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E6=95=B0=E6=89=93=E5=8D=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 4 +- kernel/Makefile | 26 ++-- kernel/{ => common}/font.h | 0 kernel/{ => common}/glib.h | 17 ++- kernel/{ => common}/printk.c | 273 ++++++++++++++++++++++++++++++----- kernel/common/printk.h | 124 ++++++++++++++++ kernel/main.c | 77 +++++++--- kernel/printk.h | 59 -------- 8 files changed, 443 insertions(+), 137 deletions(-) rename kernel/{ => common}/font.h (100%) rename kernel/{ => common}/glib.h (90%) rename kernel/{ => common}/printk.c (55%) create mode 100644 kernel/common/printk.h delete mode 100644 kernel/printk.h diff --git a/.vscode/settings.json b/.vscode/settings.json index ae1c262b..182aab42 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "files.associations": { "stdlib.h": "c", "stdbool.h": "c", - "printk.h": "c" + "printk.h": "c", + "stdarg.h": "c", + "font.h": "c" } } \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index bb739432..6854d4da 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,19 +1,27 @@ +SUBDIR_ROOTS := . common +DIRS := . $(shell find $(SUBDIR_ROOTS) -type d) +GARBAGE_PATTERNS := *.o *.s~ *.s *.S~ *.c~ *.h~ kernel *.a +GARBAGE := $(foreach DIR,$(DIRS),$(addprefix $(DIR)/,$(GARBAGE_PATTERNS))) all: kernel objcopy -I elf64-x86-64 -S -R ".eh_frame" -R ".comment" -O binary kernel ../bin/kernel/kernel.bin -kernel: head.o main.o - ld -b elf64-x86-64 -o kernel head.o main.o -T link.lds - -main.o: main.c -# -fno-builtin: 不使用C语言内建函数 -# The -m64 option sets int to 32bits and long and pointer to 64 bits and generates code for AMD’s x86-64 architecture. - gcc -mcmodel=large -fno-builtin -m64 -c main.c +kernel: head.o main.o printk.o + ld -b elf64-x86-64 -z muldefs -o kernel head.o main.o printk.o -T link.lds head.o: head.S gcc -E head.S > head.s # 预处理 as --64 -o head.o head.s -clean: - rm -rf *.o *.s~ *.s *.S~ *.c~ *.h~ kernel \ No newline at end of file +main.o: main.c +# -fno-builtin: 不使用C语言内建函数 +# The -m64 option sets int to 32bits and long and pointer to 64 bits and generates code for AMD’s x86-64 architecture. + gcc -mcmodel=large -fno-builtin -m64 -c main.c -fno-stack-protector + + +printk.o: common/printk.c + gcc -mcmodel=large -fno-builtin -m64 -c common/printk.c -fno-stack-protector + +clean: + rm -rf $(GARBAGE) \ No newline at end of file diff --git a/kernel/font.h b/kernel/common/font.h similarity index 100% rename from kernel/font.h rename to kernel/common/font.h diff --git a/kernel/glib.h b/kernel/common/glib.h similarity index 90% rename from kernel/glib.h rename to kernel/common/glib.h index 0c459b32..cb458d98 100644 --- a/kernel/glib.h +++ b/kernel/common/glib.h @@ -4,9 +4,11 @@ // #pragma once +#ifndef GLIB_H +#define GLIB_H //引入对bool类型的支持 -#include +#include #define NULL 0 @@ -24,6 +26,8 @@ #define io_lfence() __asm__ __volatile__("lfence\n\t" :: \ : "memory") // 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。 +#define ABS(x) ((x) > 0 ? (x) : -(x)) // 绝对值 + //链表数据结构 struct List { @@ -74,20 +78,21 @@ static inline void list_del(struct List *entry) entry->next = entry->prev; } -static inline bool list_empty(struct List* entry) +static inline bool list_empty(struct List *entry) { /** * @brief 判断循环链表是否为空 * @param entry 入口 */ - if(entry->prev == entry->next) + if (entry->prev == entry->next) return true; - else return false; + else + return false; } //计算字符串的长度(经过测试,该版本比采用repne/scasb汇编的运行速度快16.8%左右) -static inline int strlen(char* s) +static inline int strlen(char *s) { register int __res = 0; while (s[__res] != '\0') @@ -96,3 +101,5 @@ static inline int strlen(char* s) } return __res; } + +#endif \ No newline at end of file diff --git a/kernel/printk.c b/kernel/common/printk.c similarity index 55% rename from kernel/printk.c rename to kernel/common/printk.c index 9a83e9b7..0b5a095d 100644 --- a/kernel/printk.c +++ b/kernel/common/printk.c @@ -3,7 +3,66 @@ // #include "printk.h" #include +//#include "linkage.h" +struct screen_info pos; + +void show_color_band(int width, int height, char a, char b, char c, char d) +{ + /** 向帧缓冲区写入像素值 + * @param address: 帧缓存区的地址 + * @param val:像素值 + */ + + for (int i = 0; i < width * height; ++i) + { + + *((char *)pos.FB_address + 0) = d; + *((char *)pos.FB_address + 1) = c; + *((char *)pos.FB_address + 2) = b; + *((char *)pos.FB_address + 3) = a; + ++pos.FB_address; + } +} + +int calculate_max_charNum(int len, int size) +{ + /** + * @brief 计算屏幕上能有多少行 + * @param len 屏幕长/宽 + * @param size 字符长/宽 + */ + return len / size; +} + +int init_printk(const int width, const int height, unsigned int *FB_address, const int FB_length, const int char_size_x, const int char_size_y) +{ + + pos.width = width; + pos.height = height; + pos.char_size_x = char_size_x; + pos.char_size_y = char_size_y; + pos.max_x = calculate_max_charNum(width, char_size_x); + pos.max_y = calculate_max_charNum(height, char_size_y); + + pos.FB_address = FB_address; + pos.FB_length = FB_length; + + pos.x = 0; + pos.y = 0; + + return 0; +} + +static int set_printk_pos(const int x, const int y) +{ + // 指定的坐标不在屏幕范围内 + if (!((x >= 0 && x <= pos.max_x) && (y >= 0 && y <= pos.max_y))) + return EPOS_OVERFLOW; + pos.x = x; + pos.y = y; + return 0; +} int skip_and_atoi(const char **s) { /** @@ -18,6 +77,22 @@ int skip_and_atoi(const char **s) } return ans; } + +void auto_newline() +{ + /** + * @brief 超过每行最大字符数,自动换行 + * + */ + if (pos.x > pos.max_x) + { + pos.x = 0; + ++pos.y; + } + if (pos.y > pos.max_y) + pos.y = 0; +} + static int vsprintf(char *buf, const char *fmt, va_list args) { /** @@ -53,11 +128,11 @@ static int vsprintf(char *buf, const char *fmt, va_list args) //清空标志位和field宽度 field_width = flags = 0; - ++fmt; bool flag_tmp = true; bool flag_break = false; + ++fmt; while (flag_tmp) { switch (*fmt) @@ -67,13 +142,7 @@ static int vsprintf(char *buf, const char *fmt, va_list args) flag_break = true; flag_tmp = false; break; - case '%': - //输出 % - *str = '%'; - ++str; - ++fmt; - flag_break = true; - break; + case '-': // 左对齐 flags |= LEFT; @@ -107,13 +176,21 @@ static int vsprintf(char *buf, const char *fmt, va_list args) break; //获取区域宽度 + field_width = -1; if (*fmt == '*') { field_width = va_arg(args, int); ++fmt; } else if (is_digit(*fmt)) + { field_width = skip_and_atoi(&fmt); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } //获取小数精度 precision = -1; @@ -137,29 +214,34 @@ static int vsprintf(char *buf, const char *fmt, va_list args) qualifier = *fmt; ++fmt; } + //为了支持lld + if (qualifier == 'l' && *fmt == 'l', *(fmt + 1) == 'd') + ++fmt; //转化成字符串 - + long long *ip; switch (*fmt) { + //输出 % + case '%': + *str++ = '%'; + + break; // 显示一个字符 case 'c': //靠右对齐 if (!(flags & LEFT)) { - while (--field_width) + while (--field_width > 0) { *str = ' '; ++str; } } - else //靠左对齐 - { - *str = (char)va_arg(args, int); - ++str; - --field_width; - } - while (--field_width) + + *str++ = (unsigned char)va_arg(args, int); + + while (--field_width > 0) { *str = ' '; ++str; @@ -208,10 +290,13 @@ static int vsprintf(char *buf, const char *fmt, va_list args) break; //以八进制显示字符串 case 'o': + flags |= SMALL; + case 'O': + flags |= SPECIAL; if (qualifier == 'l') - write_num(str, va_arg(args, long long), 8, field_width, precision, flags); + str = write_num(str, va_arg(args, long long), 8, field_width, precision, flags); else - write_num(str, va_arg(args, int), 8, field_width, precision, flags); + str = write_num(str, va_arg(args, int), 8, field_width, precision, flags); break; //打印指针指向的地址 @@ -222,7 +307,7 @@ static int vsprintf(char *buf, const char *fmt, va_list args) flags |= PAD_ZERO; } - write_num(str, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + str = write_num(str, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); break; @@ -230,34 +315,35 @@ static int vsprintf(char *buf, const char *fmt, va_list args) case 'x': flags |= SMALL; case 'X': + flags |= SPECIAL; if (qualifier == 'l') - write_num(str, va_arg(args, long long), 16, field_width, precision, flags); + str = write_num(str, va_arg(args, long long), 16, field_width, precision, flags); else - write_num(str, va_arg(args, int), 16, field_width, precision, flags); + str = write_num(str, va_arg(args, int), 16, field_width, precision, flags); break; //打印十进制有符号整数 case 'i': case 'd': - case 'ld': + flags |= SIGN; if (qualifier == 'l') - write_num(str, va_arg(args, long long), 10, field_width, precision, flags); + str = write_num(str, va_arg(args, long long), 10, field_width, precision, flags); else - write_num(str, va_arg(args, int), 10, field_width, precision, flags); + str = write_num(str, va_arg(args, int), 10, field_width, precision, flags); break; //打印十进制无符号整数 case 'u': if (qualifier == 'l') - write_num(str, va_arg(args, unsigned long long), 10, field_width, precision, flags); + str = write_num(str, va_arg(args, unsigned long long), 10, field_width, precision, flags); else - write_num(str, va_arg(args, unsigned int), 10, field_width, precision, flags); + str = write_num(str, va_arg(args, unsigned int), 10, field_width, precision, flags); break; //输出有效字符数量到*ip对应的变量 case 'n': - long long *ip; + if (qualifier == 'l') ip = va_arg(args, long long *); else @@ -269,18 +355,20 @@ static int vsprintf(char *buf, const char *fmt, va_list args) //对于不识别的控制符,直接输出 default: *str++ = '%'; - if(*fmt) + if (*fmt) *str++ = *fmt; - else --fmt; + else + --fmt; break; } } *str = '\0'; + //返回缓冲区已有字符串的长度。 - return str-buf; + return str - buf; } -static void write_num(char *str, long long num, int base, int field_width, int precision, int flags) +static char *write_num(char *str, long long num, int base, int field_width, int precision, int flags) { /** * @brief 将数字按照指定的要求转换成对应的字符串 @@ -296,7 +384,6 @@ static void write_num(char *str, long long num, int base, int field_width, int p // 首先判断是否支持该进制 if (base < 2 || base > 36) return 0; - char pad, sign, tmp_num[100]; const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -335,11 +422,11 @@ static void write_num(char *str, long long num, int base, int field_width, int p tmp_num[js_num++] = '0'; else { - num = abs(num); + num = ABS(num); //进制转换 - while (num) + while (num > 0) { - tmp_num[js_num++] = num % base; // 注意这里,输出的数字,是小端对齐的。低位存低位 + tmp_num[js_num++] = digits[num % base]; // 注意这里,输出的数字,是小端对齐的。低位存低位 num /= base; } } @@ -351,7 +438,7 @@ static void write_num(char *str, long long num, int base, int field_width, int p // 靠右对齐 if (!(flags & LEFT)) - while (field_width--) + while (field_width-- > 0) *str++ = pad; if (sign) @@ -371,11 +458,119 @@ static void write_num(char *str, long long num, int base, int field_width, int p *str++ = '0'; } - while (js_num--) + while (js_num-- > 0) *str++ = tmp_num[js_num]; + - while (field_width--) + while (field_width-- > 0) *str++ = ' '; return str; +} + +static void putchar(unsigned int *fb, int Xsize, int x, int y, unsigned int FRcolor, unsigned int BKcolor, unsigned char font) +{ + /** + * @brief 在屏幕上指定位置打印字符 + * + * @param fb 帧缓存线性地址 + * @param Xsize 行分辨率 + * @param x 左上角列像素点位置 + * @param y 左上角行像素点位置 + * @param FRcolor 字体颜色 + * @param BKcolor 背景颜色 + * @param font 字符的bitmap + */ + + unsigned char *font_ptr = font_ascii[font]; + unsigned int *addr; + + int testbit; // 用来测试某位是背景还是字体本身 + + for (int i = 0; i < pos.char_size_y; ++i) + { + // 计算出帧缓冲区的地址 + addr = fb + Xsize * (y + i) + x; + testbit = (1 << (pos.char_size_x + 1)); + for (int j = 0; j < pos.char_size_x; ++j) + { + //从左往右逐个测试相应位 + testbit >>= 1; + if (*font_ptr & testbit) + *addr = FRcolor; // 字,显示前景色 + else + *addr = BKcolor; // 背景色 + + ++addr; + } + ++font_ptr; + } +} + +int printk_color(unsigned int FRcolor, unsigned int BKcolor, const char *fmt, ...) +{ + /** + * @brief 格式化打印字符串 + * + * @param FRcolor 前景色 + * @param BKcolor 背景色 + * @param ... 格式化字符串 + */ + + va_list args; + va_start(args, fmt); + + int len = vsprintf(buf, fmt, args); + + va_end(args); + unsigned char current; + + int i; // 总共输出的字符数 + for (i = 0; i < len; ++i) + { + current = *(buf + i); + //输出换行 + if (current == '\n') + { + pos.x = 0; + ++pos.y; + } + else if (current == '\t') // 输出制表符 + { + int space_to_print = 8 - pos.x % 8; + + while (space_to_print--) + { + putchar(pos.FB_address, pos.width, pos.x * pos.char_size_x, pos.y * pos.char_size_y, BLACK, BLACK, ' '); + ++pos.x; + + auto_newline(); + } + } + else if (current == '\b') // 退格 + { + --pos.x; + if (pos.x < 0) + { + --pos.y; + if (pos.y <= 0) + pos.x = pos.y = 0; + else + pos.x = pos.max_x; + } + + putchar(pos.FB_address, pos.width, pos.x * pos.char_size_x, pos.y * pos.char_size_y, FRcolor, BKcolor, ' '); + ++pos.x; + + auto_newline(); + } + else + { + putchar(pos.FB_address, pos.width, pos.x * pos.char_size_x, pos.y * pos.char_size_y, FRcolor, BKcolor, current); + ++pos.x; + auto_newline(); + } + } + + return i; } \ No newline at end of file diff --git a/kernel/common/printk.h b/kernel/common/printk.h new file mode 100644 index 00000000..306be535 --- /dev/null +++ b/kernel/common/printk.h @@ -0,0 +1,124 @@ +// +// Created by longjin on 2022/1/21. +// +#pragma once +#ifndef PRINTK_H +#define PRINTK_H + +#define PAD_ZERO 1 // 0填充 +#define LEFT 2 // 靠左对齐 +#define RIGHT 4 // 靠右对齐 +#define PLUS 8 // 在正数前面显示加号 +#define SPACE 16 +#define SPECIAL 32 // 在八进制数前面显示 '0o',在十六进制数前面显示 '0x' 或 '0X' +#define SMALL 64 // 十进制以上数字显示小写字母 +#define SIGN 128 // 显示符号位 + +#define is_digit(c) ((c) >= '0' && (c) <= '9') // 用来判断是否是数字的宏 + +// 字体颜色的宏定义 +#define WHITE 0x00ffffff //白 +#define BLACK 0x00000000 //黑 +#define RED 0x00ff0000 //红 +#define ORANGE 0x00ff8000 //橙 +#define YELLOW 0x00ffff00 //黄 +#define GREEN 0x0000ff00 //绿 +#define BLUE 0x000000ff //蓝 +#define INDIGO 0x0000ffff //靛 +#define PURPLE 0x008000ff //紫 + +// 异常的宏定义 +#define EPOS_OVERFLOW 1 // 坐标溢出 +#define EFB_MISMATCH 2 // 帧缓冲区与指定的屏幕大小不匹配 + +#include "font.h" +#include "glib.h" +//#include "linkage.h" +#include + +struct screen_info +{ + int width, height; //屏幕大小 + + int max_x, max_y; // 最大x、y字符数 + + int x, y; //光标位置 + + int char_size_x, char_size_y; + + unsigned int *FB_address; //帧缓冲区首地址 + unsigned long FB_length; // 帧缓冲区长度 +}; + + +extern unsigned char font_ascii[256][16]; //导出ascii字体的bitmap(8*16大小) ps:位于font.h中 + +char buf[4096]; //vsprintf()的缓冲区 + +/** + * @brief 初始化printk的屏幕信息 + * + * @param width 屏幕宽度 + * @param height 屏幕高度 + * @param FB_address 帧缓冲区地址 + * @param FB_length 帧缓冲区长度 + * @param char_size_x 字符的列坐标 + * @param char_size_y 字符的行坐标 + */ +int init_printk(const int width, const int height, unsigned int *FB_address, const int FB_length, const int char_size_x, const int char_size_y); +/** + * @brief Set the printk pos object + * + * @param x 列坐标 + * @param y 行坐标 + */ +static int set_printk_pos(const int x, const int y); +/** + * @brief 将字符串按照fmt和args中的内容进行格式化,然后保存到buf中 + * + * @param buf 结果缓冲区 + * @param fmt 格式化字符串 + * @param args 内容 + * @return 最终字符串的长度 + */ +static int vsprintf(char *buf, const char *fmt, va_list args); + +/** + * @brief 将数字按照指定的要求转换成对应的字符串(2~36进制) + * + * @param str 要返回的字符串 + * @param num 要打印的数值 + * @param base 基数 + * @param field_width 区域宽度 + * @param precision 精度 + * @param flags 标志位 + */ +static char* write_num(char *str, long long num, int base, int field_width, int precision, int flags); + +/** + * @brief 在屏幕上指定位置打印字符 + * + * @param fb 帧缓存线性地址 + * @param Xsize 行分辨率 + * @param x 左上角列像素点位置 + * @param y 左上角行像素点位置 + * @param FRcolor 字体颜色 + * @param BKcolor 背景颜色 + * @param font 字符的bitmap + */ +static void putchar(unsigned int *fb, int Xsize, int x, int y, unsigned int FRcolor, unsigned int BKcolor, unsigned char font); + +/** + * @brief 格式化打印字符串 + * + * @param FRcolor 前景色 + * @param BKcolor 背景色 + * @param ... 格式化字符串 + */ + + +#define printk(...) printk_color( WHITE, BLACK, __VA_ARGS__ ) + +int printk_color(unsigned int FRcolor, unsigned int BKcolor, const char*fmt, ...); + +#endif \ No newline at end of file diff --git a/kernel/main.c b/kernel/main.c index 1c816296..155bb8ea 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -1,41 +1,70 @@ // // Created by longjin on 2022/1/20. // -int *address = (int *)0xffff800000a00000; //帧缓存区的地址 -void show_color_band(int width, int height, char a, char b, char c, char d) +#include "common/glib.h" +#include "common/printk.h" + +int *FR_address = (int *)0xffff800000a00000; //帧缓存区的地址 + +void show_welcome() { - /** 向帧缓冲区写入像素值 - * @param address: 帧缓存区的地址 - * @param val:像素值 + /** + * @brief 打印欢迎页面 + * */ - for (int i = 0; i < width * height; ++i) - { - - *((char *)address + 0) = d; - *((char *)address + 1) = c; - *((char *)address + 2) = b; - *((char *)address + 3) = a; - ++address; - } + printk("\n\n"); + for (int i = 0; i < 74; ++i) + printk(" "); + printk_color(0x00e0ebeb, 0x00e0ebeb, " \n"); + for (int i = 0; i < 74; ++i) + printk(" "); + printk_color(BLACK, 0x00e0ebeb, " Welcome to DragonOS ! \n"); + for (int i = 0; i < 74; ++i) + printk(" "); + printk_color(0x00e0ebeb, 0x00e0ebeb, " \n"); } +void test_printk() +{ + //测试直接输出 + printk("\nTesting printk...\n"); + //测试输出单个字符 + printk("%c\n", 't'); + //测试输出字符串%s + printk("%s\n", "xxx"); + + //测试输出数字 + printk("%d %ld %lld\n", 1, 2, 3); + + //测试输出两个百分号 + printk("%%\n"); + + //测试输出\t + printk("\nTesting tab...\n"); + printk("date\t\tname\tscore\n"); + printk("2022-01-01\tDavid\t99\n"); + printk("2022-01-01\tJohn\t95\n"); + + + //测试输出八进制 + printk("\nTest base 8 : %d --> %o\n", 255, 255); + + //测试输出十六进制 + printk("\nTest base 16 : %d --> %x\n", 255, 255); + printk("\nTest base 16 : %d --> %X\n", 255, 255); +} //操作系统内核从这里开始执行 void Start_Kernel(void) { + // 初始化printk + init_printk(1440, 900, FR_address, 1440 * 900 * 4, 8, 16); + + show_welcome(); + test_printk(); - - - show_color_band(1440, 20, 0x00, 0xff, 0x00, 0x00); - - show_color_band(1440, 20, 0x00, 0x00, 0xff, 0x00); - - show_color_band(1440, 20, 0x00, 0x00, 0x00, 0xff); - - show_color_band(1440, 20, 0x00, 0xff, 0xff, 0xff); - while (1) ; } diff --git a/kernel/printk.h b/kernel/printk.h deleted file mode 100644 index 154bd7d6..00000000 --- a/kernel/printk.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// Created by longjin on 2022/1/21. -// -#pragma once - -#define PAD_ZERO 1 // 0填充 -#define LEFT 2 // 靠左对齐 -#define RIGHT 4 // 靠右对齐 -#define PLUS 8 // 在正数前面显示加号 -#define SPACE 16 -#define SPECIAL 32 // 在八进制数前面显示 '0o',在十六进制数前面显示 '0x' 或 '0X' -#define SMALL 64 // 十进制以上数字显示小写字母 -#define SIGN 128 // 显示符号位 - - -#define is_digit(c) ((c) >= '0' && (c) <= '9') // 用来判断是否是数字的宏 - -#include "font.h" -#include "glib.h" -#include - -struct screen_info -{ - int width, height; //屏幕大小 - - int x, y; //光标位置 - - int char_size_x, char_size_y; - - unsigned int *FB_address; //帧缓冲区首地址 - unsigned long FB_length; // 帧缓冲区长度 -} pos; - -extern unsigned char font_ascii[256][16]; //导出ascii字体的bitmap(8*16大小) - -char buf[4096]; //vsprintf()的缓冲区 - - -/** - * 将字符串按照fmt和args中的内容进行格式化,然后保存到buf中 - * @param buf 结果缓冲区 - * @param fmt 格式化字符串 - * @param args 内容 - * @return 最终字符串的长度 - */ -static int vsprintf(char *buf, const char *fmt, va_list args); - - -/** - * @brief 将数字按照指定的要求转换成对应的字符串(2~36进制) - * - * @param str 要返回的字符串 - * @param num 要打印的数值 - * @param base 基数 - * @param field_width 区域宽度 - * @param precision 精度 - * @param flags 标志位 - */ -static void write_num(char* str, long long num, int base, int field_width, int precision, int flags); \ No newline at end of file