printf的學習

首先從網上找到了glibc的標準庫的內容,找到了裏面的printf.c的函數實現
int
__printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);

  return done;
}

以前一直沒有仔細看這個函數,她的返回值是一個整型,(其實就是返回成功在現實屏上輸出的字符個數);她帶有。。。額。。。不知道該說幾個參數,第一個參數是一個字符串類型的指針常量(相當於一個數組的開頭),然後是省略號...;這是變參的表示方法,表示我們具體有幾個參數不確定,參數的個數是由第一個參數決定的(即第一個參數裏面的內容可以決定後面一共有幾個參數);

然後是 va_list arg;這一句,包含在頭文件裏面,這是一種變參列表的類型,可以用來保存字符串中的字符和變量之間的關係

接下來是
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
其中va_start (arg, format);是把字符串format裏面的內容提取參數到arg變參列表裏面
done = vfprintf (stdout, format, arg);這裏是調用另外一個現實函數vsprintf,把arg包含變參列表的內容以format格式輸出到stdout(標準輸出,PC上默認是電腦屏幕)
va_end (arg);結束轉化

這是標準庫裏的實現,但是我們才使用嵌入式的時候還可以做一些改動,把標註輸出到屏幕改成輸出到UART串口上;
爲了避免移植的時候要包括很多不知道的函數和頭文件(一個沒有包含就會出錯,相互調用的太多了),所以就參考了網上以爲大牛的博客,他是自己寫了一個pritnf函數,親測在linux ubuntu 15.10下可用

先貼上他的代碼(其中我加了點註釋和對效果的檢查)
#include
#include

int my_printf(const char* string,...);

int main()
{
int x;

my_printf("the x is :%d\n", x);

my_printf("hello world!\n");

return 0;
}

int my_printf(const char* string,...)
{

char buffer[BUFSIZ];//Be tolerant with my_printf, never try to jump ovet the band of the buffer -- buffer overflow

int temp = 0;
va_list arg;

char* p_string = NULL;
char* p_buffer = buffer;
char* p_temp   = NULL;

int counter = 0; //the value is to return the numbers of the characters put into the 'puts' succeessfully (can be ignored)
int number  = 0;
int foo     = 0;

va_start(arg,string);

for(counter = 0,p_string = string;*(p_string) != '\0';)
{
switch(*p_string)
{
case '%':
p_string++;

switch(*p_string)
{
case 'd': //the value is a number

temp = va_arg(arg,int);

foo = temp;

while(foo)
{
number++;
counter++;
foo /= 10;
}

foo = temp;

while(number)
{
*(p_buffer+number-1) = (foo);
foo /= 10;
number--;
}

p_buffer += number;
break;

case 'c': //the value is a character
temp = va_arg(arg,int);
*(p_buffer++) = temp;
break;

case 's': //the value is a string of characters
p_temp = va_arg(arg,char*);

while(p_temp != NULL)
{
*(p_buffer++) = *(p_temp++);
counter++;
}
break;

default:
break;

}
break;

default:
        *(p_buffer++) = *(p_string++);
        counter++;
}
  }

va_end(arg);

p_buffer = NULL;

puts(buffer);

return counter;
}

下面說說具體思路,同標準庫的printf,首先進行參數列表的提取,然後判斷接下來的字符是否代表參數,如果是參數則改變原來的字符串,判斷完整個字符串後(字符串長度可能會改變),則結束參量轉換,最後把得到的 新的字符串用其它方式輸出,比如可以用串口,或者其它的print顯示函數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章