定義一個不定參數的函數
int my_printf(const char *fmt, ...)
不定參數存放在哪
傳遞參數是依次存放在棧中傳遞的,不定參數存放在固定參數的後面
怎麼讀取不定參數的數值
記憶體中不定參數,4位元組對齊,對于指針變量,隻儲存指針變量的指針所指位址。
可以定義一些宏來對不定參數的讀取進行操作,這些宏在後面會用到
typedef char * va_list;
//資料類型的長度計算,四位元組對齊
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//把ap指針移動到v的最後面
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//傳回ap内的數值,然後将ap指針後移
//#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//防止野指針的存在
#define va_end(ap) ( ap = (va_list)0 )
函數簡單的舉例
int printf(const char *fmt, ...)
{
char str[110] = {0};
uint8_t offset = 0;
va_list ap;
va_start(ap, fmt);
for(; *fmt != '\0'; fmt++)
{
if (*fmt != '%') {
offset = load_c(str,offset,*fmt);
continue;
}
fmt++;
switch(*fmt)
{
case 'u':
offset = load_data(str,offset ,va_arg(ap, unsigned int));
break;
case 's':
offset = load_string(str,offset ,va_arg(ap, char *));
break;
case 'f':
offset = load_fdata(str,offset ,va_arg(ap, unsigned int));
break;
default:
offset = load_c(str,offset,*fmt);
break;
}
if(offset >= 100)
break;
}
va_end(ap);
uart_send_str(str);
return 0;
}
上面函數所調用的函數
/*
* value: 要轉換的整數,string: 轉換後的字元串,radix: 轉換進制數,如2,8,10,16 進制等。
*/
static char* itoa(int num,char* str,int radix)
{
char index[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//索引表
unsigned unum;//存放要轉換的整數的絕對值,轉換的整數可能是負數
int i=0,j,k;//i用來訓示設定字元串相應位,轉換之後i其實就是字元串的長度;轉換後順序是逆序的,有正負的情況,k用來訓示調整順序的開始位置;j用來訓示調整順序時的交換。
//擷取要轉換的整數的絕對值
if(radix==10&&num<0)//要轉換成十進制數并且是負數
{
unum=(unsigned)-num;//将num的絕對值賦給unum
str[i++]='-';//在字元串最前面設定為'-'号,并且索引加1
}
else unum=(unsigned)num;//若是num為正,直接指派給unum
//轉換部分,注意轉換後是逆序的
do
{
str[i++]=index[unum%(unsigned)radix];//取unum的最後一位,并設定為str對應位,訓示索引加1
unum/=radix;//unum去掉最後一位
}while(unum);//直至unum為0退出循環
str[i]='\0';//在字元串最後添加'\0'字元,c語言字元串以'\0'結束。
//将順序調整過來
if(str[0]=='-') k=1;//如果是負數,符号不用調整,從符号後面開始調整
else k=0;//不是負數,全部都要調整
char temp;//臨時變量,交換兩個值時用到
for(j=k;j<=(i-1)/2;j++)//頭尾一一對稱交換,i其實就是字元串的長度,索引最大值比長度少1
{
temp=str[j];//頭部指派給臨時變量
str[j]=str[i-1+k-j];//尾部指派給頭部
str[i-1+k-j]=temp;//将臨時變量的值(其實就是之前的頭部值)賦給尾部
}
return str;//傳回轉換後的字元串
}
/*
* str傳入的數組基礎位址,offset數組的偏移,c傳入的字元
* 傳回填入字元後的偏移值
*/
static uint8_t load_c(char *str,uint8_t offset ,char c)
{
* (str + offset) = c;
offset ++;
return offset;
}
/*
* str傳入的數組基礎位址,offset數組的偏移,data傳入的數字
* 傳回填入字元後的偏移值
*/
static uint8_t load_data(char *str,uint8_t offset ,uint32_t data)
{
char datastr[17] = {0};
itoa(data,datastr,10);
uint8_t len = strlen(datastr);
for(uint8_t i = 0 ; i < len ; i ++)
{
* (str + offset) = datastr[i];
offset ++;
}
return offset;
}
/*
* str傳入的數組基礎位址,offset數組的偏移,data傳入的數字
* 傳回填入字元後的偏移值
* 與load_data不同的是,這個函數會吧數字保留一位小數裝填
*/
static uint8_t load_fdata(char *str,uint8_t offset ,uint32_t data)
{
char datastr[17] = {0};
itoa(data,datastr,10);
uint8_t len = strlen(datastr);
if(len == 1)
{
* (str + offset++) = datastr[0];
* (str + offset++) = '.';
* (str + offset++) = '0';
}
else if(len > 1)
{
for(uint8_t i = 0 ; i < len ; i ++)
{
* (str + offset++) = datastr[i];
if( i == (len - 2))
* (str + offset++) = '.';
}
}
return offset;
}
/*
* str傳入的數組基礎位址,offset數組的偏移,addstr傳入的字元串
* 傳回填入字元後的偏移值
*/
static uint8_t load_string(char *str,uint8_t offset ,char *addstr)
{
uint8_t len = strlen(addstr);
for(uint8_t i = 0 ; i < len ; i ++)
{
* (str + offset) = addstr[i];
offset ++;
}
return offset;
}
my_printf函數試驗
char a = 1;
char b = 21;
char str[20] = {0};
strcpy(str,"hello world!")
my_printf("a = %u , b = %f , str = %s" , a , b , str );
//輸出結果 a = 1 , b = 2.1 ,str = hello world!