printf函数:
栈是从内存的高地址向低地址生长的,函数参数压栈顺序是从右到左,printf的第一个参数就是那个字符指针即为被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数(%5.4lf等等)来判断参数个数及数据类型。例如printf("%d,%d",a,b);汇编代码为:
.section .data string out = "%d,%d" push b push a push $out call printf
参数是最后的先压入栈中,最先的后压入栈中,参数控制的那个字符串常量是最后被压入的
几个宏:
C中变长实参头文件stdarg.h提供了一个数据类型va_list和三个宏(va_start、va_arg和va_end),用它们在被调用函数不知道参数个数和类型时对可变参数表进行测试,从而为访问可变参数提供了方便且有效的方法。va_list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va_list的变量,该变量用来指向va_arg和va_end所需信息的位置。下面给出va_list在C中的源码:
typedef char * va_list;
void va_start(va-list ap,lastfix)是一个宏,它使va_list类型变量ap指向被传递给函数的可变参数表中的第一个参数,在第一次调用va_arg和va_end之前,必须首先调用该宏。va-start的第二个参数lastfix是传递给被调用函数的最后一个固定参数的标识符。
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //得到可变参数中第一个参数的首地址
type va_arg(va_list ap,type)也是一个宏,其有双重目的,第一个是返回ap所指对象的值,第二个是修改参数指针ap使其增加以指向表中下一个参数。va_arg的第二个参数提供了修改参数指针所必需的信息。在第一次使用va_arg时,它返回可变参数表中的第一个参数,后续的调用都返回表中的下一个参数,下面给出va_arg在C中的源码:
#define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) ) //将参数转换成需要的类型,并使ap指向下一个参数
注意:type如果依次为依次为char型、char * 型、int型和float型时,在va-arg中它们的类型则应分别为int、char *、int和double.(对齐原则)
void va-end(va-list ap)也是一个宏,该宏用于被调用函数完成正常返回,功能就是把指针ap赋值为0,使它不指向内存的变量。下面给出va_end在C中的源码:
#define va_end(ap) ( ap = (va_list)0 )
va-end必须在va-arg读完所有参数后再调用,否则会产生意想不到的后果。
例子:
#include<iostream> using namespace std; #include<stdarg.h> int sum(int n,...) { int i , sum = 0; va_list vap; va_start(vap , n); //指向可变参数表中的第一个参数 for(i = 0 ; i < n ; ++i) sum += va_arg(vap , int); //取出可变参数表中的参数,并修改参数指针vap使其增加以指向表中下一个参数 va_end(vap); //把指针vap赋值为0 return sum; } int main(void) { int m = sum(3 , 45 , 89 , 72); cout<<m<<endl; return 0; }
注意:
int f(...) { ...... ...... ...... }
不能这样定义,因为这是ANSI C 所要求的,至少得定义一个固定参数。这个参数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值
例子:
#include<stdio.h> #include<stdarg.h> void myitoa(int n, char str[], int radix) //将整数表达成字符形态 { int i , j , remain; char tmp; i = 0; do //例如n = 25,radix = 10(表达成10进制), { remain = n % radix; if(remain > 9) str[i] = remain - 10 + 'A'; //为了十六进制,10将表示成A else str[i] = remain + '0'; //将整数+'0' = 整数对应的ASCII码 i++; }while(n /= radix); str[i] = '