print.h
- #ifndef __PRINT_H_
- #define __PRINT_H_
- void print(char* fmt, ...);
- void printch(char ch);
- void printdec(int dec);
- void printflt(double flt);
- void printbin(int bin);
- void printhex(int hex);
- void printstr(char* str);
- #define console_print(ch) putchar(ch)
- #endif /*#ifndef __PRINT_H_*/
上面print函数为全功能的打印函数,可以实现类似printf的功能,printch实现单个字符的打印、printdec实现十进制格式数字的打印,printflt实现浮点数的打印,printbin实现二进制格式数字的打印,printhex实现十六进制格式数字的打印,printstr实现字符串的打印,console_print函数是串口单字符打印函数的宏定义,这里暂时用PC终端单字符打印函数putchar代替。在实际嵌入式环境下,替换成串口单字符打印函数即可。
print.c
- #include <stdio.h>
- #include <stdarg.h>
- #include "print.h"
- int main(void)
- {
- print("print: %c\n", 'c');
- print("print %d\n", 1234567);
- print("print: %f\n", 1234567.1234567);
- print("print: %s\n", "string test");
- print("print: %b\n", 0x12345ff);
- print("print: %x\n", 0xabcdef);
- print("print: %%\n");
- return 0;
- }
- void print(char* fmt, ...)
- {
- double vargflt = 0;
- int vargint = 0;
- char* vargpch = NULL;
- char vargch = 0;
- char* pfmt = NULL;
- va_list vp;
- va_start(vp, fmt);
- pfmt = fmt;
- while(*pfmt)
- {
- if(*pfmt == '%')
- {
- switch(*(++pfmt))
- {
- case 'c':
- vargch = va_arg(vp, int);
- /* va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI
- mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */
- printch(vargch);
- break;
- case 'd':
- case 'i':
- vargint = va_arg(vp, int);
- printdec(vargint);
- break;
- case 'f':
- vargflt = va_arg(vp, double);
- /* va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI
- mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */
- printflt(vargflt);
- break;
- case 's':
- vargpch = va_arg(vp, char*);
- printstr(vargpch);
- break;
- case 'b':
- case 'B':
- vargint = va_arg(vp, int);
- printbin(vargint);
- break;
- case 'x':
- case 'X':
- vargint = va_arg(vp, int);
- printhex(vargint);
- break;
- case '%':
- printch('%');
- break;
- default:
- break;
- }
- pfmt++;
- }
- else
- {
- printch(*pfmt++);
- }
- }
- va_end(vp);
- }
- void printch(char ch)
- {
- console_print(ch);
- }
- void printdec(int dec)
- {
- if(dec==0)
- {
- return;
- }
- printdec(dec/10);
- printch( (char)(dec%10 + '0'));
- }
- void printflt(double flt)
- {
- int icnt = 0;
- int tmpint = 0;
- tmpint = (int)flt;
- printdec(tmpint);
- printch('.');
- flt = flt - tmpint;
- tmpint = (int)(flt * 1000000);
- printdec(tmpint);
- }
- void printstr(char* str)
- {
- while(*str)
- {
- printch(*str++);
- }
- }
- void printbin(int bin)
- {
- if(bin == 0)
- {
- printstr("0b");
- return;
- }
- printbin(bin/2);
- printch( (char)(bin%2 + '0'));
- }
- void printhex(int hex)
- {
- if(hex==0)
- {
- printstr("0x");
- return;
- }
- printhex(hex/16);
- if(hex < 10)
- {
- printch((char)(hex%16 + '0'));
- }
- else
- {
- printch((char)(hex%16 - 10 + 'a' ));
- }
- }
编译执行结果如下:
- print: c
- print: 1234567
- print: 1234567.123456
- print: string test
- print: 0b1001000110100010111111111
- print: 0xabcdef
- print: %
如上所示,print函数实现了类似printf的功能。
总结:
1、函数参数的传递原理
函数参数是以数据结构:栈的形式存取,从右至左入栈,因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
2、下面是 <stdarg.h> 里面重要的几个宏定义如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
实现步骤:
a、va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
b、然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
c、接着调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置。
d、 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL。