1函数声明

首先,要实现类似printf()的变参函数,函数的最后一个参数要用...表示,如

int log(char * arg1, ...)

这样编译器才能知道这个函数是变参函数。这个参数与变参函数的内部实现完全没有关系,只是让编译器在编译调用此类函数的语句时不计较参数多少老老实实地把全部参数压栈而不报错,当然...之前至少要有一个普通的参数,这是由实现手段限制的。

2函数实现

C语言通过几个宏实现变参的寻址。下面是linux2.18内核源码里这几个宏的定义,相信符合C89,C99标准的C语言基本都是这样定义的。

typedef char *va_list;

/*

Storage alignment properties --堆栈按机器字对齐

*/

#define _AUPBND(sizeof (acpi_native_uint) - 1)

#define _ADNBND(sizeof (acpi_native_uint) - 1)

/*

Variable argument list macro definitions --变参函数内部实现需要用到的宏

*/

#define _bnd(X, bnd)(((sizeof (X)) + (bnd)) & (~(bnd)))

#define va_arg(ap, T)(*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

#define va_end(ap)(void) 0

#define va_start(ap, A)(void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

下面以x86 32位机为例分析这几个宏的用途

要理解这几个宏需要对C语言如何传递参数有一定了解。与PASCAL相反,与stdcall相同,C语言传递参数时是用push指令从右到左将参数逐个压栈,因此C语言里通过栈指针来访问参数。虽然X86的push一次可以压2,4或8个字节入栈,C语言在压参数入栈时仍然是机器字的size为最小单位的,也就是说参数的地址都是字对齐的,这就是_bnd(X,bnd)存在的原因。另外补充一点常识,不管是汇编还是C,编译出的X86函数一般在进入函数体后立即执行

push ebp

mov ebp, esp

这两条指令。首先把ebp入栈,然后将当前栈指针赋给ebp,以后访问栈里的参数都使用ebp作为基指针。

一一解释这几个宏的作用。

l_bnd(X,bnd),计算类型为X的参数在栈中占据的字节数,当然是字对齐后的字节数了。acpi_native_unit是一个机器字,32位机的定义是:typedef u32 acpi_native_uint;

显然,_AUPBND,_ADNBND的值是4-1 == 3 == 0x00000003,按位取反( ~(bnd))就是0xfffffffc。

因此,_bnd(X,bnd)宏在32位机下就是

( (sizeof(X) + 3)&0xfffffffc )

很明显,其作用是--倘若sizeof(X)不是4的整数倍,去余加4。

_bnd(sizeof(char),3) == 4

_bnd(sizeof(struct size7struct),3) == 8

lva_start(ap,A),初始化参数指针ap,将函数参数A右边第一个参数的地址赋给ap。A必须是一个参数的指针,所以此种类型函数至少要有一个普通的参数啊。像下面的例子函数,就是将第二个参数的指针赋给ap。

lva_arg(ap,T),获得ap指向参数的值,并使ap指向下一个参数,T用来指明当前参数类型。

注意((ap) += (_bnd (T, _AUPBND)))是被一对括号括起来的,然后才减去(_bnd (T, _ADNBND),

而_AUPBND和_ADNBND是相等的。所以取得的值是ap当前指向的参数值,但是先给ap加了当前参数在字对齐后所占的字节数,使其指向了下一个参数。

lva_end(ap),作用是美观。

3总结

先用一个...参数声明函数是变参函数,接下来在函数内部以va_start(ap,A)宏初始化参数指针,然后就可以用va_arg(ap,类型)从左到右逐个获取参数值了

分析到此处算是一清二白了,下面给一个例子

int log(char * fmt,...)

{

va_list ap;

int d;

char c, *p, *s;

va_start(ap, fmt);

while (*fmt) {

switch(*fmt++) {

case 's':/* string */

s = va_arg(ap, char *);

printf("string %s\n", s);

break;

case 'd':/* int */

d = va_arg(ap, int);

printf("int %d\n", d);

break;

case 'c':/* char */

c = va_arg(ap, char);

printf("char %c\n", c);

break;

}

}

va_end(ap);

}

c语言bnd文件,(((sizeof (X)) + (bnd)) (~(bnd)))相关推荐

  1. c语言bnd文件,Unix环境下嵌入式C程序编译

    A.预编译部分 1.预编译DB2篇 1.1 什么是DB2预编译 在我们用C语言编写访问DB2的程序过程中,需要用到嵌入式SQL,其作用是将DB2 SQL混入一般C程序中, DB2预编译器可以将SQL语 ...

  2. linux c 判断文件打开文件,Linux 用C语言判断文件和文件夹

    Linux 用C语言判断文件和文件夹 #include #include #include #include int access(const char *pathname, int mode); i ...

  3. 【C 语言】文件操作 ( 配置文件读写 | 写出或更新配置文件 | 追加键值对数据 | 更新键值对数据 )

    文章目录 一.追加键值对数据 二.更新键值对数据 三.完整代码示例 一.追加键值对数据 在上一篇博客 [C 语言]文件操作 ( 配置文件读写 | 写出或更新配置文件 | 逐行遍历文件文本数据 | 获取 ...

  4. 【C 语言】文件操作 ( 学生管理系统 | 命令行接收数据填充结构体 | 结构体写出到文件中 | 查询文件中的结构体数据 )

    文章目录 一.学生管理系统 二.代码示例 一.学生管理系统 前两篇博客 [C 语言]文件操作 ( 将结构体写出到文件中并读取结构体数据 | 将结构体数组写出到文件中并读取结构体数组数据 ) [C 语言 ...

  5. C语言多文件编程基本格式

    1.背景: 用一个丢骰子的简单案例熟悉了C语言多文件编程该咋写 2.格式 (1)主函数文件main.c //文件头part1:所有要使用的函数#include ;#include ;#include ...

  6. c语言文件分类二进制,C语言实现文件版(二进制文件版)通讯录

    C语言实现文件版(二进制文件版)通讯录 C语言实现文件版(二进制文件版)通讯录 通讯录功能 添加,删除,查找,修改, 全部, 储存 文章目录通讯录功能 文件结构 一.主函数文件(入口) 二.函数声明文 ...

  7. C语言之文件读写探究(四):fwrite、fread(一次读写一块数据(二进制操作))

    相关博文:C语言之文件读写探究(一):fopen.fclose(文件的打开和关闭) 相关博文:C语言之文件读写探究(二):fputc.fgetc.feof(一次读写一个字符(文本操作)) 相关博文:C ...

  8. 【C语言程序设计进阶-浙大翁恺】C语言笔记 文件

    [C语言程序设计进阶-浙大翁恺]C语言笔记 文件 文件 格式化输入输出 文件输入输出 二进制文件 位运算 按位运算 移位运算 位运算例子 位段 文件 格式化输入输出 %-nd:数字左对齐,且输出要占n ...

  9. C语言复习——文件操作以及各种输入输出

    翁老师说现在很少有人直接用C语言来操作文件来储存或者读取数据了,稍微有点量的用数据库.个人觉得C语言实现文件操作还是挺有意思的.做个练习--一个简单的学生信息系统 头文件 #ifndef _STUDE ...

最新文章

  1. 那些方式可以合并php数组,合并数组(PHP)
  2. NLP的神经网络训练的新模式
  3. Windows Server 2008 R2之管理Sysvol文件夹
  4. postgresql 客户端_Postgresql体系结构
  5. 回调函数 相当于线程_阿里面试题:请简述下 Node 的线程模型
  6. 建立图片服务器的注意事项
  7. AOS编排语言系列教程(三):创建子网Subnet
  8. java命令行参数_Java学习从入门到精通,JDK工具条知识点学习资料
  9. Linux下使用Nginx端口转发出现502错误的一种解决办法
  10. Linux版本的tomcat安装包
  11. vue读取本机的excel文件的两种方式
  12. PMP考试重点总结二——启动过程组
  13. windows照片查看器没了_装机必备|Windows 上用得最爽的18款高效软件
  14. 第九届蓝桥杯快速排序java
  15. 【C语言】快速排序psort函数的用法及自主实现psort函数
  16. Pandas 多个工作表、工作簿
  17. 小米手机Android怎么截屏,小米手机怎么截屏 5种截屏方法分享
  18. 交通银行一直显示服务器繁忙,交通银行信用卡人工服务一直繁忙
  19. 【行业应用案例】区块链+珠宝应用案例分析
  20. 激光传感器构建栅格地图

热门文章

  1. html整数正则,javascript正则怎么判断是否整数?
  2. autobank渗流分析计算教程_autobank渗流分析计算教程_gsas结构精修软件的安装教程...
  3. 联盟链FISCO BCOS权限控制一览
  4. 华为U8860刷机包 CM11 Andorid 4.4.4 稳定流畅 推荐长期使用
  5. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day7】 —— 数据库2(事务)
  6. 网易游戏QA:在提高游戏用户和收入方面,有哪些经验?
  7. dynet的一个基本介绍(1):动态神经网络工具包Dynet
  8. python是什么职业_Python是个什么鬼?为什么那么多工作“会Python优先”?
  9. 关于主播表现能力的调研
  10. 利用D415读取 需要标记的人脸face_recognition的距离 Python + wind10