Linux 下自写printf函数实现参数不固定及其原理(用C语言实现,其他语言勿扰)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 下自写printf函数实现参数不固定及其原理(用C语言实现,其他语言勿扰)相关的知识,希望对你有一定的参考价值。

/*
*printf.c - print formatted
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines printf() - print formatted data
*
*******************************************************************************/

#include <cruntime.h>
#include <stdio.h>
#include <dbgint.h>
#include <stdarg.h>
#include <file2.h>
#include <internal.h>
#include <mtdll.h>

/***
*int printf(format, ...) - print formatted data
*
*Purpose:
* Prints formatted data on stdout using the format string to
* format data and getting as many arguments as called for
* Uses temporary buffering to improve efficiency.
* _output does the real work here
*
*Entry:
* char *format - format string to control data format/number of arguments
* followed by list of arguments, number and type controlled by
* format string
*
*Exit:
* returns number of characters printed
*
*Exceptions:
*
*******************************************************************************/

int __cdecl printf (
const char *format,
...
)
/*
* stdout 'PRINT', 'F'ormatted
*/

va_list arglist;
int buffing;
int retval;

va_start(arglist, format);

_ASSERTE(format != NULL);

#ifdef _MT
_lock_str2(1, stdout);
__try
#endif /* _MT */

buffing = _stbuf(stdout);

retval = _output(stdout,format,arglist);

_ftbuf(buffing, stdout);

#ifdef _MT

__finally
_unlock_str2(1, stdout);

#endif /* _MT */

return(retval);
参考技术A func(1,2,3,4,5,0)
函数入栈方式由高向低

<---栈底
5
4
3
2
1
ip<---函数调用需要返回的位置
bp<---保存bp寄存器 执行push BP;mov bp,sp;的结果,这个有参函数的自己处理的
<----函数使用的栈空间, sp指向这里,bp同样指向这里

现在 bp 上边除了bp和ip两个位置就是参数
bp的下边就是函数中局部变量的空间
函数使用bp[offset]来得到参数,和处理函数内部的局部变量
printf是通过第一个参数来获得信息,处理其他参数的
所以必须要告诉函数参数如何结束.
有的用第一个参数,有的用最后一个参数为零
函数退出时pop bp;恢复bp的值
这时候函数返回,返回到ip指向的位置

这些东西需要会点汇编才能搞清楚
看8懂偶也没有办法啦~~
原理就是如此,只是用16位做个例子,
c语言的可以参考stdarg.h中的实现,本质是一样的
参考技术B 楼上烧饼
兰州你百度下不就好了

可变参数使用

在C中,可变参数用于参数个数,类型不确定的情况,如printf,snprintf函数的实现。

当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

void func(...);
void func(parm_list,...);

这是C传参的一种形式,与固定参数不同。

函数参数的传递原理

函数参数以栈的形式存储,从右往左入栈。
举个例子:

void func(int x, float y, char z);

在调用函数的时候,实参z先入栈,然后是y,最后是x。在内存中变量的存放次序是x->y->z。所以,从理论上来说,只要知道x,y,z其中一个变量的地址和类型,通过指针运算,可找到其他变量。

typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type ); 
void va_end ( va_list ap ); 

va_list是指向当前参数的指针,通过这个指针进行取参。
宏的使用方式如下:

  1. 先定义一个va_list的变量,假设为ap
  2. 使用va_start对ap进行初始化,va_start的第一个参数是ap,第二个参数是变参表中“…”前面的那个参数
  3. 然后调用va_arg获取参数,第一个参数还是ap,第二个参数是要获取的参数的类型,并且ap指向下一个变量
  4. 获取完参数后,使用va_end关掉ap。va_start和va_end通常成对出现。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int my_snprintf(char *dst, int size, char *fmt, ...)
{
    int len;
    va_list argp;
    va_start(argp, fmt);
    len = vsnprintf(dst, size, fmt, argp);
    len = len > size - 1 ? size - 1 : len;
    va_end(argp);
    
    return len;
}

int main(void)
{
    char str[8];
    int len;
    
    len = my_snprintf(str, sizeof(str), "A:%d:%s", 1, "ABCDEFGH");
    printf("str:%s, len:%d\n", str, len);

    return 0;
}

以上是关于Linux 下自写printf函数实现参数不固定及其原理(用C语言实现,其他语言勿扰)的主要内容,如果未能解决你的问题,请参考以下文章

99,printf scanf手动功能实现

c 中可变参数的实现

gnu printf可变参数宏

CSS 布局实现左边固定宽度,右边占满剩余宽度

格式化字符串攻击原理及示例

页面布局方案-上固定,下自适应