zoukankan      html  css  js  c++  java
  • 一步一步写嵌入式操作系统5—变参函数的原理和实现1 转

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

    2011-10-30 10:25:49|  分类: ARM学习 |  标签:arm操作系统  |字号 订阅

     

    一步一步写嵌入式操作系统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);,当然,肯定

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

  • 相关阅读:
    [loj6271]生成树求和
    [cf1209E]Rotate Columns
    [cf1491H]Yuezheng Ling and Dynamic Tree
    [atARC064F]Rotated Palindromes
    [cf1491G]Switch and Flip
    [cf1491F]Magnets
    [atARC063F]Snuke's Coloring 2
    [atARC062F]Painting Graphs with AtCoDeer
    [atARC061F]Card Game for Three
    [atARC112E]Rvom and Rsrev
  • 原文地址:https://www.cnblogs.com/shenlian/p/2276337.html
Copyright © 2011-2022 走看看