C语言函数是从右到左入栈的

va_list ap;//=char *ap;(一个字符指针)

va_start(ap,v) 中 ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )//返回参数v后的参数列表地址(V地址+V的长度)

va_arg(ag,type):va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )//取指定类型的值int i=va_arg(ap,int),并且使ap指向向一个参数

va_end(ag):清空参数列表,并置参数指针ag无效。说明:指针ag被置无效后,可以通过调用 va_start()、va_arg恢复arg。每次调用va_start() / va_arg()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内

注:

其中:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1)

~是位取反的意思。_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。

比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。((5+4-1)/4)*4=8;

~(sizeof(int) – 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) – 1) )后最后两位肯定为0,就肯定是4的整数倍了。

(sizeof(n) + sizeof(int) – 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) – 1) )后就正好将原长度补齐到4的倍数了

即:

((5+4-1)/4)

(sizeof(n) + sizeof(int) – 1)&~(sizeof(int-1)

栈底 高地址 
    | .......      
    | 函数返回地址 
    | .......       
    | 函数最后一个参数 
    | ....                        
    | 函数第一个可变参数       <--va_start后ap指向  
    | 函数最后一个固定参数 
    | 函数第一个固定参数  
    栈顶 低地址

Linux内核中,用两个非常巧妙地宏实现了,一个是offsetof宏,另一个是container_of宏,下面讲解一下这两个宏。

1.  offsetof宏
【定义】:#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
【功能】: 获得一个结构体变量成员在此结构体中的偏移量。
【例子】:
struct A
{
int x ;
int y;
int z;
};
void main()
{
printf("the offset of z is %d",offsetof( struct A, z )  );
}
// 输出结果为 8
【分析】:
该宏,TYPE为结构体类型,MEMBER 为结构体内的变量名。
(TYPE *)0) 是欺骗编译器说有一个指向结构TYPE 的指针,其地址值0
(TYPE *)0)->MEMBER 是要取得结构体TYPE中成员变量MEMBER的地址. 因为基址为0,所以,这时MEMBER的地址当然就是MEMBER在TYPE中的偏移了

转载于:https://www.cnblogs.com/fujinliang/archive/2012/11/24/2785348.html

va_list和va_start和((A*)0)-a相关推荐

  1. va_list、va_start和va_end使用

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1.硬件平台的不同 2.编译器的不同,所以定义的宏也有所不同. 在ANSI C中,这些宏的定义位于stdar ...

  2. 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法

     参考: http://www.360doc.com/content/12/0309/10/4025635_192940551.shtml http://www.cnblogs.com/Annie ...

  3. va_list、va_start和va_end的用法

    在项目的代码中看到函数不定参数的使用,现在总结一下va_list.va_start和va_end三个宏的用法. c语言提供了函数的不定长参数使用,比如 void func(int a, -).三个省略 ...

  4. va_start、va_end、va_list的使用

    http://www.cnblogs.com/rainduck/archive/2010/11/10/1873417.html 1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表 v ...

  5. va_list/va_start/va_end的使用

    va_list 键入以保存有关变量参数的信息 va_start 初始化变量参数列表 初始化ap以检索参数paramN后面的附加参数. 调用va_start的函数在返回之前也应调用va_end. 参数不 ...

  6. va_list va_start va_end的使用

    <pre name="code" class="cpp" style="color: rgb(51, 51, 51); white-space: ...

  7. C语言使用函数参数传递中的省略号:va_list, va_start, va_arg, va_end

    首先要处理这种省略号的参数的话,需要包含头文件#include <stdarg.h>,然后利用下面的函数对"..."省略号变量进行处理. va_list arg; ty ...

  8. 可变参C API va_list,va_start,va_arg_va_end以及c++可变参模板

    文章目录 C变参API C变参API函数原型 C变参API实现源码 C变参API应用实例 C 变参函数缺点 C++变参实现 方法 initializer_list 形参 可变参数模板 C变参API C ...

  9. 变长参数va_list va_start va_arg va_end

    对于int printf(const char *format, ...);这种变长参数,需要使用va_list va_start va_end va_arg来访问参数. 下面是一个tutorials ...

最新文章

  1. draw.io基础使用
  2. Oracle 11g R2手动配置EM(转)
  3. Spring Boot 入门与实战笔记
  4. JAVA Servlet API简介及接口与类的用法
  5. 2.2基本算法之递归和自调用函数_数据结构与算法之5——队列和栈
  6. 【转】The underlying connection was closed
  7. 清除Linux和window等系统的DNS缓存的命令
  8. 百度文库解决复制问题
  9. poi合并单元格,没有样式
  10. 应广单片机长按开关机_单键实现单片机开关机
  11. http301重定向IIS 301重定向域名转向新旧域名更替
  12. android socket 长连接_java-socket长连接demo体验
  13. ATF:Gicv源码文件系列-gicdv2_helpers.c
  14. 运维笔记(三)服务器介绍和XShell使用
  15. java计算机毕业设计技术的游戏交易平台源代码+数据库+系统+lw文档
  16. 【好书推荐】《只有偏执狂才能生存》—安迪.格鲁夫
  17. TROY,加密资产新范式下的基础设施
  18. Pass by reference和pass by value区别举例
  19. OC中的非正式协议与正式协议的区别
  20. 海龟绘图turtle模块

热门文章

  1. matlab极大值点个数,求一组数的极大值个数
  2. aptana手动配置python环境_Aptana Studio 3中通过Auto Config配置PyDev中Python出错
  3. C++_泛型编程与标准库(八)
  4. js变量传递给less_如何利用webpack实现一键换肤(CSS变量替换)
  5. Elasticsearch之倒排索引
  6. iOS 字符属性NSAttributedString描述
  7. 大佬告诉你JavaScript面试题大全之基础面试题(附答案)
  8. 公办低分二本_三本4所二本3所,四川考生都是用这7所大学保底的,分数低保录取...
  9. mysql 添加table_mysql数据库对table的增删改查
  10. redisson的锁的类型_利用Redisson实现分布式锁,并防止重复提交