定义一个不定参数的函数
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!