编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急)

来源:学生作业帮助网 编辑:作业帮 时间:2024/11/25 22:45:09
编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急)
xYmS+dlI Hb'ENgts7kjz6[v}W,/h\$uX̮.7+UqLNa'mijo"5%^eEݭo:ɮ~_j:;ͣ"_?ܻ<5Zk4bw 8٘['E!XKk/l5R./2w;egyqFsg^Nl@8<x.1ryk>fZ,Ha! ,(%sgXo*SU۞]˷D v+A|nF ɽ񒯔HɁ`b&}WdXs wN{$Aff刿֭)W ߪ98?ܮ.xxO@-UO*i`%XꐴrfY/KaIF }NEZT {o]Ȥxjhڭ-^~)]}Ba vL)*L57u bxc 1u^XE߇-MW2eMҖ#DGo K:NLH=i51zɡA &NKCjyI֖~Q'`!>"qWK l" 4ŷO{",m@TJ0$gC¯ @!:tl4XD!Hi",q/FqM{yX>S$I?9?;$4.8AMb ZSڃJL 12LpKa#(fÄ?Q__g b#.LU7\ѓHy,xHtGND@!#TK*S)+9WD{ôPkE7;_GO,6 =y5ē]Q-e+ ^{GC|/d(xFCCO[!w۵XC(@9, 2_Cb+CK8ഖQQ8;yRN?H?J<ZOjmsk~C["KY;#]9?{=pMjFɺ]Pk#CX`WEEѕd} Xa:{ FY9>tY,?ymc l}N{o$rTd$"Gl/VP]/g/Rr|טnj\eb(b'ٻXD?-|ayU¿D8 b bw"? Yī4Dv:2/_!K ꄏa,+ᕼ|?0u؟t @,z^rWfdu)1og-H{t|Ϸg9 耤bF]j:ք4wPK 7>GS5^+ѧ^e:4 "0שY1hïW!ŚJpĀ7.B^JpYγf/{BSyK$hhV^ Alw9rJzݹQ:@>,H]]nu×v}-n( dzl_s:ԍ[-NN+`ݼйYupp eAIZ}Lcзvf+NrSY5C,إK| {/]]󛔠{ZdsSbYy-'s7md 5.48 ul&ȧU@yCgRLcUiB? )OY`.5P#LlT(uN\S_E Xp z{SBؼ6֤f^V܄)dSΨS)|N6Eh k `m֞J>SXYm6Q/~(l=&꺂ǛO^=Tjpn$4)*,6W,O2S{,, "]cmT|<DԋI, so|0|x>0<0__}a_EQ|cl7լGoq Q\

编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急)
编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急)

编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急)
在嵌入式系统中,串口常用来辅助调试输出一些调试信息.所以有必要实现串口的格式化输出功能,这可以由3种方法实现(就我目所知).
1)使用系统库函数printf(),这就需要重载输入,输出函数int fputc(int ch, FILE *f);int fgetc(FILE *f).
2)使用sprintf()函数将数据格式化到数组,然后将数组输出.也可以使用vsprintf().
3)自己写类似printf()函数.
这里假设已经编写了基本的输入输出函数如下:
int sendchar(int ch); // 发送字符
int sendstr(char *str);//发送字符窜
int getkey(void); // 接受字符
1)第一种方法的实现
比较简单只要实现以下2个函数:
int fputc(int ch, FILE *f) {
return (sendchar(ch));
}
int fgetc(FILE *f) {
return (getkey());
}
使用方法:
#include
void main()
{
...
printf("%s,%d,%x","hello",0x10,0x10);//输出: hello,16,10
...
}
2)第二种方法的实现
void Uart_Printf(const char *fmt,...)
{
va_list ap;
char string[256];
va_start(ap,fmt);
vsprintf(string,fmt,ap);
Uart_SendString(string);
va_end(ap);
}
3)第三种方法的实现
int myprintf (const char* str,...)
{
va_list arp;
uint8 c, f, r;
ULONG val;
char s[16];
int i, w, res, cc;
va_start(arp, str);
for (cc = res = 0; cc != EOF; res += cc) {
c = *str++;
if (c == 0) break; /* End of string */
if (c != '%') { /* Non escape cahracter */
sendchar(c);
cc=1; //cc保存当前循环发送的数据
continue;
}
w = f = 0; //f为格式 ,w为宽度
c = *str++;
if (c == '0') { /* Flag: '0' padding */
f = 1;
c = *str++; //等于c = *(str++);先取值,最后str++ ;f的第0位代表用0填充对齐 c = *(str++);
}
while (c >= '0' && c = 0x80000000) { //最高位为1说明是负的
val = 0 - val;
f |= 4;//f的第三位代表"+/-"
}
}
i = sizeof(s) - 1; s[i] = 0;//i=15
do {
c = (uint8)(val % r + '0');//r代表进制
if (c > '9') c += 7; //对于16进制 转换到对应的'abc...'需要+7 :'\58'+7='65'='a'
s[--i] = c; //从后面开始保存
val /= r;
} while (i && val);
if (i && (f & 4)) s[--i] = '-';//是负数添加'-'号
w = sizeof(s) - 1 - w;
while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
cc = sendstr(&s[i]);
}
va_end(arp);
return (cc == EOF) ? cc : res;
}
变参数表的调用形式以及原理:
参数在内存中存放格式:大多数情况堆栈是从高到低生长,函数参数入栈时后面的参数先入栈.参数弹出顺序与入栈顺序相反.
举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量.
下面是MDK 里面重要的几个宏定义如下:
typedef struct __va_list { void *__ap; } va_list;
/*
* an array type suitable for holding information needed by the macro va_arg
* and the function va_end. The called function shall declare a variable
* (referred to as ap) having type va_list. The variable ap may be passed as
* an argument to another function.
* Note: va_list is an array type so that when an object of that type
* is passed as an argument it gets passed by reference.
*/
#define va_arg(ap, type) __va_arg(ap, type)
/*
* The va_arg macro expands to an expression that has the type and value of
* the next argument in the call. The parameter ap shall be the same as the
* va_list ap initialised by va_start. Each invocation of va_arg modifies
* ap so that successive arguments are returned in turn. The parameter
* 'type' is a type name such that the type of a pointer to an object that
* has the specified type can be obtained simply by postfixing a * to
* 'type'. If type is a narrow type, an error is given in strict ANSI
* mode, or a warning otherwise. If the type is an array or function type,
* an error is given.
* In non-strict ANSI mode, 'type' is allowed to be any expression.
* Returns: The first invocation of the va_arg macro after that of the
* va_start macro returns the value of the argument after that
* specified by parmN. Successive invocations return the values of
* the remaining arguments in succession.
* The result is cast to 'type', even if 'type' is narrow.
*/
#define va_end(ap) ((void)(ap))
/*
* The va_end macro facilitates a normal return from the function whose
* variable argument list was referenced by the expansion of va_start that
* initialised the va_list ap. If the va_end macro is not invoked before
* the return, the behaviour is undefined.
* Returns: no value.
*/
使用方法:
在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯.
va_list的"等价"替换:
va_list arp; {char *arp = 0;}
va_start(arp, str);{ arp = (char *)&str; arp += sizeof(str); }
sendstr(va_arg(arp, type)); { sendstr( *(type*)arp); arp += sizeof(type);}
va_end(arp); { arp = 0;}
因此上述函数可以改为:
int myprintf ( const char* str, ...)
{
//va_list arp;
char *arp = 0;
uint8 c, f, r;
ULONG val;
char s[16];
int i, w, res, cc;
//va_start(arp, str);
arp = (char *)&str;
arp += sizeof(str);
for (cc = res = 0; cc != EOF; res += cc) {
c = *str++;
if (c == 0) break; /* End of string */
if (c != '%') { /* Non escape cahracter */
sendchar(c);
cc=1; //cc保存当前循环发送的数据
continue;
}
w = f = 0; //f为格式 ,w为宽度
c = *str++;
if (c == '0') { /* Flag: '0' padding */
f = 1;
c = *str++; //等于c = *(str++);先取值,最后str++ ;f的第0位代表用0填充对齐 c = *(str++);
}
while (c >= '0' && c = 0x80000000) { //最高位为1说明是负的
val = 0 - val;
f |= 4;//f的第三位代表"+/-"
}
}
i = sizeof(s) - 1; s[i] = 0;//i=15
do {
c = (uint8)(val % r + '0');//r代表进制
if (c > '9') c += 7; //对于16进制 转换到对应的'abc...'需要+7 :'\58'+7='65'='a'
s[--i] = c; //从后面开始保存
val /= r;
} while (i && val);
if (i && (f & 4)) s[--i] = '-';//是负数添加'-'号
w = sizeof(s) - 1 - w;
while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
cc = sendstr(&s[i]);
}
//va_end(arp);
arp = 0;
return (cc == EOF) ? cc : res;
}

编写print函数以取代标准库函数printf,在print函数中不许使用printf函数.(急) C语言的函数可分为主函数,标准库函数和啥? 用户可以重新定义标准库函数,若如此,该函数将失去原有定义? 编写一个FUN函数:long fun(char s[]),将一个数字字符串转换为一个整数(不得调用标准库函数,转换仅限于正整数).例如,若输入字符串“1234”,则函数把他转换为整数1234.1.输入输出的功能在ma c语言,编写一个fun函数:long fun(char s【】),将一个数字字符串转换成一个整数(不得调换标准库函数转换仅限于正整数).例如,输入字符串啊“1234”则函数转换成为整数1234!还不行!那么回 编写一个函数实现两个字符串的连接(不使用库函数strcat).这个用C语言怎么写程序啊求详细解释~急! 函数 主函数 被调用函数 库函数是什么 对字符串进行操作的标准库函数有哪些? C语言如何编一个函数比较两个字符串的大小能用字符串函数编写吗?··谢谢了··需要自己编写比较大小的函数··不能用库函数那一个·(用数组名作为函数参数,编写一个比较两个字符串s和t 编写一个函数,求标准的一元二次方程ax2+bx+c=0的根,要求方程的系数都可以以主程序输入来改变? C语言main函数与其他函数之间的关系包括库函数和自定义函数? print,traverse 均为函数 traverse(print)是什么意思 C++ 用MATLAB编写函数:编写一个函数文件,以向量为参数,对向量元素进行排序并输出 print Print print print 求Qt---C++库函数、类函数手册,介绍某个函数是干嘛的