注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

一步一步写嵌入式操作系统5—变参函数的原理和实现1  

2011-10-30 10:25:49|  分类: ARM学习 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一步一步写嵌入式操作系统5—变参函数的原理和实现
我们写操作系统,当然很多的库就用不到了,可能很多人都不习惯,不过,有一些东西我们是可以自己来实现的,其实包括经典的printf这个函数,可是,这个函数是变参函数,

也就是说,你输入的形参的个数是不固定的,于是就产生了一个问题,怎么才能实现这种函数呢,是不是所有的情况下都可以实现这种变参函数呢?
首先我们想一下经常用到的调用形式,不外乎这几种,__stdcall,__pascal,__fastcall,__classcall,__cdecl,这其除了最后一个是调用者自己管理堆栈外,其它都是被调用者自

己管自己,而这恰恰就是是否可以形成变参的一个重要的因素,也就是说,只有__cdecl这种调用者管理堆栈的方式才可能会出现变参。
变参的实现我们以一个小例子来实现:

typedef char * va_list;
#define _INTSIZEOF(n)   ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap)    ( ap = (va_list)0 )

void test_num(int num){
 *(char *)0xd0000020=num+'0';
}

void test_vparameter(int i,...){
 int mm;
 va_list argv;
 va_start(argv,i);
 while(i--){
  mm=va_arg(argv,int);
  test_num(mm);
 }
 va_end(argv);
}
程序很简单,关键在宏的使用上,_INTSIZEOF宏提供了内存中字节对齐的具体的数据的长度,比如int 是4,double是 8,这个宏就是生成一个比要计算的数大并能整除4的最小的那

个数,其实就是为了字节对齐。va_start用于计算第一个固定的参数的内存中的位置。va_arg宏用于根据va_start和_INTSIZEOF来不断的计算变参的位置以得出其值(即返回当前

值并将指针移到下一个参数的位置),va_end宏用于清理整个过程。
于是这就产生了一个规矩,即变参的函数必须有一个固定的参数,两行不行捏,这还用说,当然行。

书上和网上都有类似于printf的小例子:
#include <stdio.h>
#include <stdlib.h>

void myprintf(char* fmt, ...)    //一个简单的类似于p rintf 的实现,参数必须都是int 类型
{
    char* pArg = NULL;  //等价于原来的 va_list
    char c;
    pArg = (char*) &fmt;  //注意不要写成 p = fmt !!因为这里要对//参数取址,而不是取值
    pArg += sizeof(fmt);  //等价于原来的va_start        

    do {
        c =*fmt;
        if (c != '%') {
            putchar(c);//照原样输出字符
        } else {  //按格式字符输出数据
            switch(*++fmt)
            {
            case 'd':
                printf("%d",*((int*)pArg));         
                break;
            case 'x':
                printf("%#x",*((int*)pArg));
                break;
            default:
                break;
            }
            pArg += sizeof(int);  //等价于原来的va_arg
        }

        ++fmt;
    } while (*fmt != '\0');

    pArg = NULL;    //等价于va_end
    return;
}

int main(int argc, char* argv[])
{
    int i = 1234;
    int j = 5678;

    myprintf("the first test: i = %d\n", i, j);
    myprintf("the secend test: i = %d; %x; j=%d;\n", i, 0xabcd, j);
    system("pause");
    return 0;
}
固定的类型和可计算出的类型,都好说,现在说说如果实现以下的函数怎么办:
void mytest(int c,...);//变参为:double,int,short
即类型在变,书上和网上目前尚未找到现成的例子,其实你看上面的类似printf这个例子就明白了,你可以自己实现一个数组或者其它的什么,把数据类型保存起,比如1代表

int,2代表short,3代表double等等,那么你也可以实现一个case,每当判断时,自动跳转,然后在case 语句中,实现mm=va_arg(argv,int);mm=va_arg(argv,short);,当然,肯定

有更好的方法,但只要懂得了原理,那么其它的具体的实现的好坏,大家就仁者见仁,智者见智。
时间过得很快,进度要一点点儿把握住。
每天前进一点点,日积月累,以汇成江海。
用魏武帝的诗来结尾:山不厌高,水不厌深。以自勉。

  评论这张
 
阅读(1081)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017