1013-----C語言----------幾個va_宏函數的使用

  最近在看APUE附帶的源碼時,看到它的錯誤處理文件中用到了可變參數列表(以下),正好最近總是看到這幾個函數在眼前晃悠,因此就作個了斷吧。哈哈。html

#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, "\n");
	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類型代替了數目不定的參數。第一種用的已經不能再熟了,主要來看第二種的用法。ui

2.幾個va_類宏函數的用法
  2.1 函數定義spa

#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 *;.net

  va_start(va_list ap, last)  //該函數用來初始化ap,使得ap指向可變參數列表的第一個參數;last變量是可變參數列表前面的那個參數; unix

  va_arg(va_list ap, type)    // 返回ap指向的參數的內容,該參數類型爲type,ap指向可變參數列表中的下一個參數;指針

  va_end(va_list ap)         //清空arg;code

  va_copy(va_list dest, va_list src) //複製參數列表指針。orm

  2.2 用法示例,這裏要注意使用該函數須要在參數列表中指出參數數目或者約定參數列表結束符,不然將會發生越界錯誤。htm

#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

相關文章
相關標籤/搜索