最近在看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, "\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类型代替了数目不定的参数。第一种用的已经不能再熟了,主要来看第二种的用法。

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);}elsebreak;}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

转载于:https://www.cnblogs.com/monicalee/p/4022732.html

1013-----C语言----------几个va_宏函数的使用相关推荐

  1. python展开 c函数中的宏预处理_C 语言常用的预处理-宏函数

    #include // 宏函数 三目运算符 #define MAX(A, B) A>B?A:B //宏函数 多行 添加\直接回车 #define LOOP(FROM, TO, CONTENT)\ ...

  2. ACMNO.33 C语言-最大值3 分别用函数和带参的宏,从三个数中找出最大的数。

    题目描述 分别用函数和带参的宏,从三个数中找出最大的数. 输入 3个实数 输出 最大的数,输出两遍,先用函数,再用宏. 保留3位小数. 样例输入 1 2 3 样例输出 3.000 3.000 来源/分 ...

  3. python展开 c函数中的宏预处理_最基本的宏函数 课后习题9.2 (C语言代码)

    解题思路 利用宏函数. 函数. 本题的难点肯定不在算法, 应该是宏函数! 带参宏定义的一般形式为:#define 宏名(形参表) 字符串; 在字符串中含有各个形参. 带参宏调用的一般形式为: #def ...

  4. c语言宏函数怎么传递宏参数_C语言中的宏参数评估

    c语言宏函数怎么传递宏参数 We can define a function like Macro, in which we can pass the arguments. When a Macro ...

  5. c语言中void和define,C语言里面的内联函数(inline)与宏定义(#define)探讨

    C语言里面的内联函数(inline)与宏定义(#define)探讨 先简明扼要,说下关键: 1.内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样 ...

  6. C语言开发必会 宏定义、宏函数

    文章目录 宏定义 宏函数 宏定义之闰年判断 宏定义 宏定义是C中三种预处理方式(宏定义.文件包含.条件编译)的一种,只做替换.不求解. 宏定义分为有参宏定义和无参宏定义两种.应特别注意有参宏定义的括号 ...

  7. C语言重载宏函数的小技巧

    在写C/C++语言时我们经常会用到宏定义,宏函数就是带参数的宏定义(blablabla--省去背景介绍). 有时候我们会需要一个可以有多种参数版本的宏定义,例如: 1 2 #define MACRO_ ...

  8. C语言中的宏函数与宏定义

    目录 1.无参宏定义 1.1 无参数宏定义的格式: 1.2 使用说明: 2.带参宏定义 2.1 带参数宏定义的格式: 2.2 使用说明: 3.带参宏定义与函数调用的区别 4.头文件中常用的宏定义 5. ...

  9. C语言宏定义、宏函数、内置宏与常用宏

    前言: 在C语言中,变量类型.循环控制.基础语法等与其他高级语言基本无异:而C语言(C++)特有的两把双刃剑指针和宏定义/宏函数使得C语言在底层开发中披荆斩棘.无所不能.这两个概念涉及范围比较广,其分 ...

  10. 关于C语言刷题(#define宏定义函数的常见错误)

    关于C语言刷题(#define宏定义函数的常见错误) 首先我们来先看对#define的定义 define,宏定义,C语言中预处理命令一种.分为无参宏定义和带参宏定义.无参宏定义的一般形式为:#defi ...

最新文章

  1. 区分HPUX是Itanium还是PA-RISC
  2. Android学习笔记进阶九之Matrix对称变换
  3. 3ds max 把模型放置到坐标系中心(原点)
  4. 2020黑群晖最稳定版本_打造完美6.2.3黑群晖,正确显示 CPU,支持Nvme缓存
  5. P2894 [USACO08FEB]酒店Hotel
  6. CSS层叠样式表进阶
  7. MongoDB学习笔记Day3
  8. oracle数据库无法显示图层,ArcMap 无法在Oracle中创建图层案例
  9. python使用selenium_如何在python中使用selenium的示例
  10. 手机子王掩码和网关查找_C程序使用位掩码查找奇数或偶数
  11. Java import static静态导入
  12. 安全威胁建模综述_如何使用威胁建模分析应用程序的安全性
  13. 改善深层神经网络:超参数调整、正则化以及优化 —— 3.2 为超范围
  14. python查看是否存在某个变量名
  15. ​8次迭代5大升级,旷视天元1.0预览版正式发布
  16. DEV MessageBox
  17. java三角形角度_利用java解决三角形角度问题
  18. 【liunxptp协议栈详解第一部分】
  19. 将安卓手机屏幕内容投射到电脑屏幕上
  20. A Survey of Deep Learning-based Object Detection论文翻译 + 阅读笔记

热门文章

  1. 今天,强行打个广告!
  2. 薄荷Android团队招聘啦
  3. Vue笔记:使用 axios 中 this 指向问题
  4. ORB:新一代 Linux 应用
  5. [转载].net常用函数
  6. android架构图示
  7. note 2 运算符和表达式
  8. HBuilderx中编译sass文件
  9. All about the “paper”
  10. 规范 : 日期在不同国家与项目之间的关系