C可变参数实现原理

在C程序设计语言中,使用printf函数进行标准输出。

int printf ( const char * format, ... );

printf函数申明中"..."代表可变参数。

printf ("floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
printf ("Width trick: %*d \n", 5, 10);

那么,如何实现可变参数?

近日,在读Linux0.12源代码的过程中,我看到了一个实现。下面,我给出一个demo程序。

#include <iostream>
using namespace std;

typedef char *va_list;

#define __va_rounded_size(TYPE)    (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

#define va_start(AP, LASTARG) 						 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))

#define va_end(AP)  

#define va_arg(AP, TYPE)						 (AP += __va_rounded_size (TYPE),					  *((TYPE *) (AP - __va_rounded_size (TYPE))))

void print_args(int args, ...)
{
    va_list ap;
    //在访问任何未命名的参数之前,必须用va_start宏初始化ap一次
    va_start(ap,args);
    printf("%d\n",args);
    printf("%d\n",va_arg(ap,int));
    printf("%s",va_arg(ap,char *));
//  va_end(ap);
}

int main(void)
{
    int arg = 2;
    int args1 = 1;
    char *args2  = "abcdefg";
    print_args(2,args1,args2);
    return 0;
}

不妨在print_args程序中设置一个断点

首先,查看一下args参数的内存地址:

+		&args	0x0028f71c	int *

现在,我们查看0x0028f71c处的内存:

0x0028F71C  02 00 00 00  ....
0x0028F720  01 00 00 00  ....
0x0028F724  08 58 cd 00  .X?.

显然, 0x0028f71c处的4个字节为0x00000002,即main函数中的arg参数;

0x0028f720处的4个字节为0x00000001,即main函数中的args1参数;

而0x0028f724处的4个字节内容为0x00cd5808,这是一个内存地址;

0x00CD5808  61 62 63 64  abcd
0x00CD580C  65 66 67 00  efg.

继续查看0x00cd5808处的内存,可以看出正是"abcdefg\0"。

有了上面的基础,我们应当可以理解va_start, va_end和va_arg宏了。实际上,就是对地址的操作,以及强制类型转换。printf函数也是利用上面3个宏实现可变参数功能的。

时间: 2024-10-18 22:27:38

C可变参数实现原理的相关文章

[Java5新特性]可变参数

什么是可变参数 Java基础内容中,关于函数具有一种特性:重载,如果我们要完成多个数字想加的需求,可以按照以下代码完成: public class Demo { public int add(int a, int b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } public static void main(String[] args) { int sum1 = new Demo().a

可变参数中size_t遇见的问题

在修改php扩展Trie时,出现了一个小bug PHP_FUNCTION(trie_filter_load) { Trie *trie; char *path; int path_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) { RETURN_NULL(); } if(path !=NULL){ php_printf("p

C语言可变参数函数实现原理

一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本文地址:http://www.cnblogs.com/archimedes/p/variable-parameter.html,转载请注明源地址. 例如,对于函数: void fun(int a, int b, int c) { int d; ... } 其栈结构为 0x1ffc-->d 0x200

C语言可变长参数实现原理

博客:存储系统研究 微博:http://weibo.com/u/2203007022             (1)      C语言可变参数 我们可以从C语言的printf得出可变参数的作用,printf函数的原型如下: int printf ( const char * format, ... ); 通过使用可变个数参数,就是传入的参数个数是可变的,如printf需要根据format实参传入多个实参. (2)      C语言可变参数的使用 下面一个函数myprintf是自己实现的比较简单的

c 可变参数(variable argument)的原理及使用

本文主要介绍可变参数的函数使用,然后分析它的原理,程序员自己如何对它们实现和封装,最后是可能会出现的问题和避免措施. VA函数(variable argument function),参数个数可变函数,又称可变参数函数.C/C++编程中,系统提供给编程人员的va函数很少.*printf()/*scanf()系列函数,用于输入输出时格式化字符串:exec*()系列函数,用于在程序中执行外部文件(main(int argc,char*argv[]算不算呢,与其说main()也是一个可变参数函数,倒不

C利用可变参数列表统计一组数的平均值,利用函数形式参数栈原理实现指针运算

//描述:利用可变参数列表统计一组数的平均值 #include <stdarg.h> #include <stdio.h> float average(int num, ...);//函数原型:即声明 float average2(int num, ...);//num个数 void add(int num, int x, int y, int z); int main(void){ int a=10; int b=20; printf("a地址:%p b地址:%p\n&

C语言中可变参数函数实现原理

C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fun(int a, int b, int c) { int d; ... } 其栈结构为 0x1ffc-->d 0x2000-->a 0x2004-->b 0x2008-->c 对于在32位系统的多数编译器,每个栈单元的大小都是sizeof(int), 而函数的每个参数都至少要占一个栈单

java可变参数列表的实现

参数就是我们调用一个方法时需要传入得数据,在方法中可能需要参数协助处理数据或者对参数进行解析处理以达到我们希望得到的数据和结果. 平常我们在写一个方法时,我们能确定需要传入什么样的参数以及参数的个数,这样我们在实现这个方法的时候在确定参数的时候都会有明确的目标.但是有时候会有这种特殊情况,我们并不知道我们将要传入几个参数,或者说我们并不确定外部会传入多少参数.在这种情况下,我们就要用到可变参数列表.下面是可变参数方法的实现方法. 1.传入数组对象或者集合,这里只对传入数组对象进行简单说明,集合的

C语言的可变参数

可变参数给编程带来了很大的方便,在享受它带来的方便的同时,很有必要了解一下其实现方式,在了解编程语言的同时,也可以扩展编程的思路. 可变参数需要用到3个宏函数和一个类型,他们都定义在<stdarg.h>中,分别是: va_start(vl) va_arg(vl, type) va_end(vl) 其中vl是va_list类型,type就是对象类型(如int, double或 自定义的struct之类的). va_start函数用来初始化vl va_arg(vl, type)用来取得type类型