zoukankan      html  css  js  c++  java
  • 1013-----C语言----------几个va_宏函数的使用

      最近在看APUE附带的源码时,看到它的错误处理文件中用到了可变参数列表(如下),正好最近老是看到这几个函数在眼前晃悠,所以就做个了断吧。哈哈。

    #include "apue.h"
    #include <errno.h>		/* for definition of errno */
    #include <stdarg.h>		/* ISO C variable aruments */
    
    static void	err_doit(int, int, const char *, va_list);
    
    /*
     * Nonfatal error related to a system call.
     * Print a message and return.
     */
    void
    err_ret(const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(1, errno, fmt, ap);
    	va_end(ap);
    }
    
    /*
     * Fatal error related to a system call.
     * Print a message and terminate.
     */
    void
    err_sys(const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(1, errno, fmt, ap);
    	va_end(ap);
    	exit(1);
    }
    
    /*
     * Fatal error unrelated to a system call.
     * Error code passed as explict parameter.
     * Print a message and terminate.
     */
    void
    err_exit(int error, const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(1, error, fmt, ap);
    	va_end(ap);
    	exit(1);
    }
    
    /*
     * Fatal error related to a system call.
     * Print a message, dump core, and terminate.
     */
    void
    err_dump(const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(1, errno, fmt, ap);
    	va_end(ap);
    	abort();		/* dump core and terminate */
    	exit(1);		/* shouldn't get here */
    }
    
    /*
     * Nonfatal error unrelated to a system call.
     * Print a message and return.
     */
    void
    err_msg(const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(0, 0, fmt, ap);
    	va_end(ap);
    }
    
    /*
     * Fatal error unrelated to a system call.
     * Print a message and terminate.
     */
    void
    err_quit(const char *fmt, ...)
    {
    	va_list		ap;
    
    	va_start(ap, fmt);
    	err_doit(0, 0, fmt, ap);
    	va_end(ap);
    	exit(1);
    }
    
    /*
     * Print a message and return to caller.
     * Caller specifies "errnoflag".
     */
    static void
    err_doit(int errnoflag, int error, const char *fmt, va_list ap)
    {
    	char	buf[MAXLINE];
    
    	vsnprintf(buf, MAXLINE, fmt, ap);
    	if (errnoflag)
    		snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
    		  strerror(error));
    	strcat(buf, "
    ");
    	fflush(stdout);		/* in case stdout and stderr are the same */
    	fputs(buf, stderr);
    	fflush(NULL);		/* flushes all stdio output streams */
    }

    1.先重温一下常用的含有可变参数的函数,以格式化输出函数族为例:

    #include <stdio.h>
    
    int printf(const char *format, ...);                //格式化到标准输出
    int fprintf(FILE *stream, const char *format, ...);        //格式化到文件
    int sprintf(char *str, const char *format, ...);        //格式化到字符串
    int snprintf(char *str, size_t size, const char *format, ...);//规定了格式化字符串的最大长度
    
    #include <stdarg.h>
    
    int vprintf(const char *format, va_list ap);                 
    int vfprintf(FILE *stream, const char *format, va_list ap);
    int vsprintf(char *str, const char *format, va_list ap);
    int vsnprintf(char *str, size_t size, const char *format, va_list ap);
    

      这两组函数的功能是一样的,区别是下面使用了va_list类型的变量,也就是说用va_list类型代替了数目不定的参数。第一种用的已经不能再熟了,主要来看第二种的用法。

    2.几个va_类宏函数的用法
      2.1 函数定义

    #include <stdarg.h>
         
           void va_start(va_list ap, last);
           type va_arg(va_list ap, type);
           void va_end(va_list ap);
           void va_copy(va_list dest, va_list src);

      va_list           //可变参数列表指针类型 ,在x86中定义为 char *;

      va_start(va_list ap, last)  //该函数用来初始化ap,使得ap指向可变参数列表的第一个参数;last变量是可变参数列表前面的那个参数; 

      va_arg(va_list ap, type)    // 返回ap指向的参数的内容,该参数类型为type,ap指向可变参数列表中的下一个参数;

      va_end(va_list ap)         //清空arg;

      va_copy(va_list dest, va_list src) //复制参数列表指针。

      2.2 用法示例,这里要注意使用该函数需要在参数列表中指出参数数目或者约定参数列表结束符,否则将会发生越界错误。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    
    int test(char *fmt, ...);
    
    int main(int argc, const char *argv[])
    {
        test("test", "hello", "world","");
        return 0;
    }
    
    int test(char *fmt, ...){
        va_list ap;
        char *temp;
    
        va_start(ap, fmt);
        while(1){
            temp = va_arg(ap, char *);
            if(temp != ""){
                printf("%s ", temp);
            }
            else
                break;
        }
    
        va_end(ap);
        return 0;
    }

    3.宏函数的实现
      通过函数的用法可以看出,这些宏函数的实现关键在于参数列表指针的移动,不同的类型移动的字节数不同,C中定义了这个宏用于判断变量的长度并且该宏函数实现了内存对齐。
    #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)

      第二个较难理解,这里需要分解

      (1)ap += _INTSIZEOF(t)     //ap指向下一个参数的地址

      (2)宏函数返回 当期参数 return *(t *)(ap - _INTSIZEOF(t))。

    4.调用一个函数时 栈帧的情况
      这里涉及寄存器的使用及部分汇编,待我这几天看一下汇编语言再总结。

      http://blog.chinaunix.net/uid-20718384-id-3418279.html

    5.参考
      http://www.360doc.com/content/10/1220/11/1317564_79712393.shtml
      http://www.360doc.com/content/10/1220/11/1317564_79711248.shtml
      http://blog.chinaunix.net/uid-23089249-id-34493.html

  • 相关阅读:
    [编程题] 基础 [位运算基础]
    [编程题] lc [191. 位1的个数]
    [编程题] lc [69. x 的平方根]
    php 中php-fpm工作原理
    redis分布式锁
    3种Redis分布式锁的对比
    php使用数据库的并发问题(乐观锁与悲观锁)
    php观察者模式应用场景实例详解
    [Usaco2008 Jan]电话网络
    关于二分图结论的一些证明
  • 原文地址:https://www.cnblogs.com/monicalee/p/4022732.html
Copyright © 2011-2022 走看看