?? vsprintf.c
字號:
/** linux/kernel/vsprintf.c** (C) 1991 Linus Torvalds*//* vsprintf.c -- Lars Wirzenius & Linus Torvalds. *//** Wirzenius wrote this portably, Torvalds fucked it up :-)*/#include <stdarg.h> // 標準參數頭文件。以宏的形式定義變量參數列表。主要說明了-個// 類型(va_list)和三個宏(va_start, va_arg 和va_end),用于// vsprintf、vprintf、vfprintf 函數。#include <string.h> // 字符串頭文件。主要定義了一些有關字符串操作的嵌入函數。/* we use this so that we can do without the ctype library *//* 我們使用下面的定義,這樣我們就可以不使用ctype 庫了 */#define is_digit(c) ((c) >= '' && (c) <= '9') // 判斷字符是否數字字符。// 該函數將字符數字串轉換成整數。輸入是數字串指針的指針,返回是結果數值。另外指針將前移。static intskip_atoi (const char **s){ int i = 0; while (is_digit (**s)) i = i * 10 + *((*s)++) - ''; return i;}// 這里定義轉換類型的各種符號常數。#define ZEROPAD 1 /* pad with zero */ /* 填充零 */#define SIGN 2 /* unsigned/signed long */ /* 無符號/符號長整數 */#define PLUS 4 /* show plus */ /* 顯示加 */#define SPACE 8 /* space if plus */ /* 如是加,則置空格 */#define LEFT 16 /* left justified */ /* 左調整 */#define SPECIAL 32 /* 0x */ /* 0x */#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */ /* 使用小寫字母 */// 除操作。輸入:n 為被除數,base 為除數;結果:n 為商,函數返回值為余數。// 參見4.5.3 節有關嵌入匯編的信息。#define do_div(n,base) ({ \int __res; \__asm__( "divl %4": "=a" (n), "=d" (__res): "" (n), "1" (0), "r" (base)); \__res; })// 將整數轉換為指定進制的字符串。// 輸入:num-整數;base-進制;size-字符串長度;precision-數字長度(精度);type-類型選項。// 輸出:str 字符串指針。static char *number (char *str, int num, int base, int size, int precision, int type){ char c, sign, tmp[36]; const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int i;// 如果類型type 指出用小寫字母,則定義小寫字母集。// 如果類型指出要左調整(靠左邊界),則屏蔽類型中的填零標志。// 如果進制基數小于2 或大于36,則退出處理,也即本程序只能處理基數在2-32 之間的數。 if (type & SMALL) digits = "0123456789abcdefghijklmnopqrstuvwxyz"; if (type & LEFT) type &= ~ZEROPAD; if (base < 2 || base > 36) return 0;// 如果類型指出要填零,則置字符變量c='0'(也即''),否則c 等于空格字符。// 如果類型指出是帶符號數并且數值num 小于0,則置符號變量sign=負號,并使num 取絕對值。// 否則如果類型指出是加號,則置sign=加號,否則若類型帶空格標志則sign=空格,否則置0。 c = (type & ZEROPAD) ? '' : ' '; if (type & SIGN && num < 0) { sign = '-'; num = -num; } else sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);// 若帶符號,則寬度值減1。若類型指出是特殊轉換,則對于十六進制寬度再減少2 位(用于0x),// 對于八進制寬度減1(用于八進制轉換結果前放一個零)。 if (sign) size--; if (type & SPECIAL) if (base == 16) size -= 2; else if (base == 8) size--;// 如果數值num 為0,則臨時字符串='0';否則根據給定的基數將數值num 轉換成字符形式。 i = 0; if (num == 0) tmp[i++] = ''; else while (num != 0) tmp[i++] = digits[do_div (num, base)];// 若數值字符個數大于精度值,則精度值擴展為數字個數值。// 寬度值size 減去用于存放數值字符的個數。 if (i > precision) precision = i; size -= precision;// 從這里真正開始形成所需要的轉換結果,并暫時放在字符串str 中。// 若類型中沒有填零(ZEROPAD)和左靠齊(左調整)標志,則在str 中首先// 填放剩余寬度值指出的空格數。若需帶符號位,則存入符號。 if (!(type & (ZEROPAD + LEFT))) while (size-- > 0) *str++ = ' '; if (sign) *str++ = sign;// 若類型指出是特殊轉換,則對于八進制轉換結果頭一位放置一個'0';而對于十六進制則存放'0x'。 if (type & SPECIAL) if (base == 8) *str++ = ''; else if (base == 16) { *str++ = ''; *str++ = digits[33]; // 'X'或'x' }// 若類型中沒有左調整(左靠齊)標志,則在剩余寬度中存放c 字符('0'或空格),見51 行。 if (!(type & LEFT)) while (size-- > 0) *str++ = c;// 此時i 存有數值num 的數字個數。若數字個數小于精度值,則str 中放入(精度值-i)個'0'。 while (i < precision--) *str++ = '';// 將轉數值換好的數字字符填入str 中。共i 個。 while (i-- > 0) *str++ = tmp[i];// 若寬度值仍大于零,則表示類型標志中有左靠齊標志標志。則在剩余寬度中放入空格。 while (size-- > 0) *str++ = ' '; return str; // 返回轉換好的字符串。}// 下面函數是送格式化輸出到字符串中。// 為了能在內核中使用格式化的輸出,Linus 在內核實現了該C 標準函數。// 其中參數fmt 是格式字符串;args 是個數變化的值;buf 是輸出字符串緩沖區。// 請參見本代碼列表后的有關格式轉換字符的介紹。intvsprintf (char *buf, const char *fmt, va_list args){ int len; int i; char *str; // 用于存放轉換過程中的字符串。 char *s; int *ip; int flags; /* flags to number() *//* number()函數使用的標志 */ int field_width; /* width of output field *//* 輸出字段寬度*/ int precision; /* min. # of digits for integers; max number of chars for from string *//* min. 整數數字個數;max. 字符串中字符個數 */ int qualifier; /* 'h', 'l', or 'L' for integer fields *//* 'h', 'l',或'L'用于整數字段 */// 首先將字符指針指向buf,然后掃描格式字符串,對各個格式轉換指示進行相應的處理。 for (str = buf; *fmt; ++fmt) {// 格式轉換指示字符串均以'%'開始,這里從fmt 格式字符串中掃描'%',尋找格式轉換字符串的開始。// 不是格式指示的一般字符均被依次存入str。 if (*fmt != '%') { *str++ = *fmt; continue; }// 下面取得格式指示字符串中的標志域,并將標志常量放入flags 變量中。/* process flags */ flags = 0; repeat: ++fmt; /* this also skips first '%' */ switch (*fmt) { case '-': flags |= LEFT; goto repeat; // 左靠齊調整。 case '+': flags |= PLUS; goto repeat; // 放加號。 case ' ': flags |= SPACE; goto repeat; // 放空格。 case '#': flags |= SPECIAL; goto repeat; // 是特殊轉換。 case '': flags |= ZEROPAD; goto repeat; // 要填零(即'0')。 }// 取當前參數字段寬度域值,放入field_width 變量中。如果寬度域中是數值則直接取其為寬度值。// 如果寬度域中是字符'*',表示下一個參數指定寬度。因此調用va_arg 取寬度值。若此時寬度值// 小于0,則該負數表示其帶有標志域'-'標志(左靠齊),因此還需在標志變量中添入該標志,并// 將字段寬度值取為其絕對值。/* get field width */ field_width = -1; if (is_digit (*fmt)) field_width = skip_atoi (&fmt); else if (*fmt == '*') {/* it's the next argument */ field_width = va_arg (args, int); if (field_width < 0) { field_width = -field_width; flags |= LEFT; } }// 下面這段代碼,取格式轉換串的精度域,并放入precision 變量中。精度域開始的標志是'.'。// 其處理過程與上面寬度域的類似。如果精度域中是數值則直接取其為精度值。如果精度域中是// 字符'*',表示下一個參數指定精度。因此調用va_arg 取精度值。若此時寬度值小于0,則// 將字段精度值取為其絕對值。/* get the precision */ precision = -1; if (*fmt == '.') { ++fmt; if (is_digit (*fmt)) precision = skip_atoi (&fmt); else if (*fmt == '*') {/* it's the next argument */ precision = va_arg (args, int); } if (precision < 0) precision = 0; }// 下面這段代碼分析長度修飾符,并將其存入qualifer 變量。(h,l,L 的含義參見列表后的說明)。/* get the conversion qualifier */ qualifier = -1; if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { qualifier = *fmt; ++fmt; }// 下面分析轉換指示符。 switch (*fmt) {// 如果轉換指示符是'c',則表示對應參數應是字符。此時如果標志域表明不是左靠齊,則該字段前面// 放入寬度域值-1 個空格字符,然后再放入參數字符。如果寬度域還大于0,則表示為左靠齊,則在// 參數字符后面添加寬度值-1 個空格字符。 case 'c': if (!(flags & LEFT)) while (--field_width > 0) *str++ = ' '; *str++ = (unsigned char) va_arg (args, int); while (--field_width > 0) *str++ = ' '; break;// 如果轉換指示符是's',則表示對應參數是字符串。首先取參數字符串的長度,若其超過了精度域值,// 則擴展精度域=字符串長度。此時如果標志域表明不是左靠齊,則該字段前放入(寬度值-字符串長度)// 個空格字符。然后再放入參數字符串。如果寬度域還大于0,則表示為左靠齊,則在參數字符串后面// 添加(寬度值-字符串長度)個空格字符。 case 's': s = va_arg (args, char *); len = strlen (s); if (precision < 0) precision = len; else if (len > precision) len = precision; if (!(flags & LEFT)) while (len < field_width--) *str++ = ' '; for (i = 0; i < len; ++i) *str++ = *s++; while (len < field_width--) *str++ = ' '; break;// 如果格式轉換符是'o',表示需將對應的參數轉換成八進制數的字符串。調用number()函數處理。 case 'o': str = number (str, va_arg (args, unsigned long), 8, field_width, precision, flags); break;// 如果格式轉換符是'p',表示對應參數的一個指針類型。此時若該參數沒有設置寬度域,則默認寬度// 為8,并且需要添零。然后調用number()函數進行處理。 case 'p': if (field_width == -1) { field_width = 8; flags |= ZEROPAD; } str = number (str, (unsigned long) va_arg (args, void *), 16, field_width, precision, flags); break;// 若格式轉換指示是'x'或'X',則表示對應參數需要打印成十六進制數輸出。'x'表示用小寫字母表示。 case 'x': flags |= SMALL; case 'X': str = number (str, va_arg (args, unsigned long), 16, field_width, precision, flags); break;// 如果格式轉換字符是'd','i'或'u',則表示對應參數是整數,'d', 'i'代表符號整數,因此需要加上// 帶符號標志。'u'代表無符號整數。 case 'd': case 'i': flags |= SIGN; case 'u': str = number (str, va_arg (args, unsigned long), 10, field_width, precision, flags); break;// 若格式轉換指示符是'n',則表示要把到目前為止轉換輸出的字符數保存到對應參數指針指定的位置 中。// 首先利用va_arg()取得該參數指針,然后將已經轉換好的字符數存入該指針所指的位置。 case 'n': ip = va_arg (args, int *); *ip = (str - buf); break;// 若格式轉換符不是'%',則表示格式字符串有錯,直接將一個'%'寫入輸出串中。// 如果格式轉換符的位置處還有字符,則也直接將該字符寫入輸出串中,并返回到107 行繼續處理// 格式字符串。否則表示已經處理到格式字符串的結尾處,則退出循環。 default: if (*fmt != '%') *str++ = '%'; if (*fmt) *str++ = *fmt; else --fmt; break; } } *str = '\0'; // 最后在轉換好的字符串結尾處添上null。 return str - buf; // 返回轉換好的字符串長度值。}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -