printf函数遵守C调用规范,即参数 从右至左 压栈,堆栈由调用者平衡。(这种从右至左的方式不会随着编译器,机器的不同而不同) )。
[code]
printf("%d,%d", i, i++ ); 产生的汇编码大致像下面这样:
mov eax,dword ptr [i]
push eax //最右边那个i的值被压入栈中
inc eax //i++
mov dword ptr[ i ], eax
mov edx,dword ptr [i]
push edx //第二个i的值被压入栈中
push offset string "%d,%d" //字符串 "%d,%d"的地址被压入栈中
call printf //跳转到函数print
add esp, 12 //平衡堆栈
[/code]
有几个小题帮助分析:
1.
#include <stdio.h>
int main()
{
char * p = "abcdefg";
printf("%c%c", *p,*(p++));
return 0;
}
输出结果:ba
2.
#include <stdio.h>
int main()
{
char * p = "abcdefg";
printf("%c%c%c", *p,*(p++), *(++p));
return 0;
}
输出结果:cbb
3.
#include <stdio.h>
int main()
{
char * p = "abcdefg";
printf("%c%c", *p,*(p+3));
return 0;
}
输出结果:ad
int printf( const char *format , ... );
最后一个(在这个函数中是第二个)参数写成“...”意思是该函数能接收不定数量的参数,或者更普遍的说法是变长参数。
以printf为例:
printf("This is a %s and a %s", s1, s2);
在这次调用中,除去第一个字符常指针,有两个实际参数s1和s2的地址。
printf("I wanna to print %d.", num);
在这次调用中,除去第一个字符常指针,有一个实际参数num。
C语言允许变长参数的具体实现,因编译器而异。多数仍然是通过栈来实现,因为参数的使用和记录都是由编译器维护
这里是printf的源代码
C/C++ code
#include <libioP.h>
#include <stdarg.h>
#include <stdio.h>
#undef printf
/* Write formatted output to stdout from the format string FORMAT. */
/* VARARGS1 */
int
__printf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
#undef _IO_printf
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )