全站数据
8 4 2 0 5 8 1

printf原理

中小学教师资格考试 | 简单学习,快乐成才!         
问题更新日期:2024-10-16 04:34:15

问题描述

printf原理,麻烦给回复
精选答案
最佳答案

printf的实现原理

printf的声明

int _cdecl printf(const char* format, …);

_cdecl是C和C++程序的缺省调用方式

_CDEDL调用约定:

1.参数从右到左依次入栈

2.调用者负责清理堆栈

3.参数的数量类型不会导致编译阶段的错误

对于x86而言,栈向下生长,函数参数从右向左入栈,因此从第一个固定参数(format)地址向前(向上)移动就可得到其他变参的地址。

va_list相关宏(VC++中stdarg.h里x86平台的宏定义)

typedef char *va_list;

//_INTSIZEOF(n)宏:将sizeof(n)按sizeof(int)对齐。

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

//取format参数之后的第一个变参地址,4字节对齐

#define va_start(va_listap, format) ( ap = (va_list)&format+ _INTSIZEOF(format) )

//对type类型数据,先取到其四字节对齐地址,再取其值

#define va_arg(va_listap,type)

( *(type*)((ap += _INTSIZEOF(type)) -_INTSIZEOF(type)) )

#define va_end(va_listap)( ap = (va_list)0 )

如何得到参数个数?

其实printf并不知道参数个数,它只是逐个解析format字符串。对于特定类型%,使用va_arg去取相应参数的值,直到遍历字符串结束。类似于如下代码:

#include <stdio.h>

#include <stdarg.h>

void myprintf(const char *format, ...)

{

va_list ap;

char ch;

va_start(ap, format);

while(ch = *format++)

{

switch(c)

{

case 'c':

{

char ch1 = va_arg(ap, char);

putchar(ch1);

break;

}

如下调用会打印什么内容?

printf("%x");

----------------

背景知识:

函数调用栈帧结构:

比如Z调用A,Z的栈帧:

1.自右向左(约定)压入参数

2.Z中返回地址。即从A返回后Z中下一条指令地址

3.调用者的EBP。由编译器插入指令实现:

"pushl %ebp"

"movl %esp, %ebp"//esp为栈指针

因而形成一个链表。依此可得到调用者的栈顶位置(对于A的EBP,得到Z的EBP地址为0x000f)。

4.局部变量。

对于两个正整数 x, n 总存在整数 q, r 使得

x = nq + r, 其中0<= r <n//最小非负剩余

q, r 是唯一确定的。q = [x], r = x - n[x]. 这个是带余除法的一个简单形式。在 c 语言中, q, r 容易计算出来: q = x, r = x % n.

所谓把 x 按 n 对齐指的是:若 r=0, 取 qn, 若 r>0, 取 (q+1)n. 这也相当于把 x 表示为:

x = nq + r', 其中 -n < r' <=0//最大非正剩余

nq 是我们所求。关键是如何用 c 语言计算它。由于我们能处理标准的带余除法,所以可以把这个式子转换成一个标准的带余除法,然后加以处理:

x+n = qn + (n+r'),其中 0<n+r'<=n//最大正剩余

x+n-1 = qn + (n+r'-1), 其中 0<= n+r'-1 <n//最小非负剩余

所以 qn = [(x+n-1)]n. 用 c 语言计算就是:

((x+n-1))*n

若 n 是 2 的方幂, 比如 2^m,则除为右移 m 位,乘为左移 m 位。所以把 x+n-1 的最低 m 个二进制位清 0就可以了。得到:

(x+n-1) & (~(n-1))

其他回答

printf函数的实现原理

在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。

随机推荐