C 可变参数函数分析(va_start,va_end,va_list...)

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=032)
  本文发布于 2017-03-14 17:12:28,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=032)

环境说明

  无

前言


  在几年前,由于兴趣需要,去研究了c的不定参数问题,当时由于太懒,没有记录任何资料,导致现在实际需要的时候,又要重新来过。呜呼哀哉,太烦了~~~~~~~





c/cpp不定参数


  首先,c不定参数所定义的宏的头文件在 stdarg.h中。在我的系统中在这里:/usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h,源码我就不贴了,就简要分析几个重要宏的原理。

//va_list  //此宏依赖与不同的平台和操作系统。
    //常用定义:(具体和编译器有关,其实了解透后,都差不多)
    typedef char *  __builtin_va_list;//(注意可能是这样定义的)
    (or)
    typedef void *  __builtin_va_list;//(注意可能是这样定义的)
    typedef __builtin_va_list __gnuc_va_list;  
    typedef __gnuc_va_list va_list;


//_INTSIZEOF(n) //此宏是windows上定义的,目的是为了考虑兼容一些内存地址对齐的系统,n应该占多少字节,如:arm指令集是4字节对齐访问,Thumb指令集是24字节对齐访问.在linux上,gcc编译器会内部定义一个类似的宏,来代表对齐的字节数。其用法是为了根据一个确定的参数,来依次确定不定参数中的每一个参数。
    #define  _INTSIZEOF(n) \
        ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    //一般在一个操作系统中,int类型所占的字节数即为当前对齐的字节数,一般为4或者8。关于此宏的详细分析,以下的分析来至于网上,带入sizeof(n) = 4,8即可明白:
    #define  _INTSIZEOF(n)  ((sizeof(n) + x) & ~(x))
    x = sizeof(int) - 1 = 3 = 0000 0000 0000 0011(b)
    ~x = 1111 1111 1111 1100(b)

//va_start(v,l) //此宏获取的是可变参数中的第一个参数的地址
    #define __builtin_va_start(v,l) \
            ( v = (va_list)&l + _INTSIZEOF(l) )//(注意可能是这样定义的),l的地址加上l地址所占的空间
    #define va_start(v,l)   __builtin_va_start(v,l)
	
//va_arg(v,l)//此宏是使下一个可变参数的地址赋值给v,同时返回开始时v地址所指向的值,注意:此时v已经指向下一个参数地址,但是表达式返回的值是初始v的值,《《这里只需要搞清楚括号优先级就能够明白》》。
    #define __builtin_va_arg(v,l) \
            ( *(l *)((v += _INTSIZEOF(l)) - _INTSIZEOF(l)) )//(注意可能是这样定义的)
    #define va_arg(v,l) __builtin_va_arg(v,l)
		
//va_end(v)//此宏处理va_list声明的一个指针,防止出现野指针。有始有终。
    #define __builtin_va_end(v)\
        ( v = (va_list)0 )
    #define va_end(v)   __builtin_va_end(v)



来个例子(t.c)
#include <stdarg.h>
#include <stdio.h>

void ttt(char * fuk,...);

int main(int argc, char * argv[]){
    ttt("fuk",2333,"this is 23333333333333333");
    return 0;
}
void ttt(char * fuk,...){

	va_list arg;
	int a;
	char *b;
	va_start(arg,fuk);
	a = va_arg(arg,int);
	b = va_arg(arg,char *);
	va_end(arg);

	printf("%d,%s\n",a,b);
}

编译

gcc t.c
./a.out
rep_img




后记


最后的注意:

  1. 变参函数必须有一个以上的指定参数
  2. 变参的《《类型》》必须通过一定方式已知(如格式化字符串,"%d,%s"等等),否则就是定死的
  3. 变参的《《个数》》也必须通过一定方式已知(如格式化字符串,"%d,%s"等等),否则就是定死的
  4. c的函数参数入栈方式是从右到左,栈的地址一般是从高到底。

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

posted on   SkyOnSky  阅读(119)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示