怎样实现自己的printf

自己实现的简易版printf 我实现的这个printf主要是运用指针和可变参数来实现常用的打印功能: 源码如下: #include #include void print_int(int argint); void print_float(double argflt); void print_small_radix(int argint,int n); void print_str(char *argstr); void 阅读详情
C中的可变参数研究

一. 何谓可变参数
int printf( const char* format, ...);
这是使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用”…”表示). 而我们又可以用各种方式来调用printf,如:
printf( "%d ",value);
printf( "%s ",str);
printf( "the number is %d ,string is:%s ", value, str);

二.实现原理
C语言用宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来分析这些宏。在VC中的stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:

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 )


以下再用图来表示:

在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。
|——————————————————————————|
|最后一个可变参数 | -> 高内存地址处
|——————————————————————————|
...................
|——————————————————————————|
|第N个可变参数 | -> va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N个可变参数的地址。
|——————————————— |
………………………….
|——————————————————————————|
|第一个可变参数 | -> va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一个可变参数的地址
|——————————————— |
|———————————————————————— ——|
| |
|最后一个固定参数 | -> start的起始地址
|—————————————— —| .................
|—————————————————————————— |
| |
|——————————————— |-> 低内存地址处


三.printf研究

下面是一个简单的printf函数的实现,参考了中的156页的例子,读者可以结合书上的代码与本文参照。
#include "stdio.h "
#include "stdlib.h "
void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型
{
char* pArg=NULL; //等价于原来的va_list
char c;

pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
pArg += sizeof(fmt); //等价于原来的va_start

do
{
c =*fmt;
if (c != '% ')
{
putchar(c); //照原样输出字符
}
else
{
//按格式字符输出数据
switch(*++fmt)
{
case 'd ':
printf( "%d ",*((int*)pArg));
break;
case 'x ':
printf( "%#x ",*((int*)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等价于原来的va_arg
}
++fmt;
}while (*fmt != '\0 ');
pArg = NULL; //等价于va_end
return;
}

int main(int argc, char* argv[])
{
int i = 1234;
int j = 5678;

myprintf( "the first test:i=%d ",i,j);
myprintf( "the secend test:i=%d; %x;j=%d; ",i,0xabcd,j);
system( "pause ");
return 0;
}


在intel+win2k+vc6的机器执行结果如下:
the first test:i=1234
the secend test:i=1234; 0xabcd;j=5678;

四.应用
求最大值:
#include //不定数目参数需要的宏
int max(int n,int num,...)
{
va_list x;//说明变量x
va_start(x,num);//x被初始化为指向num后的第一个参数
int m=num;
for(int i=1;i {
//将变量x所指向的int类型的值赋给y,同时使x指向下一个参数
int y=va_arg(x,int);
if(y> m)m=y;
}
va_end(x);//清除变量x
return m;
}

int main()
{
printf( "%d,%d ",max(3,5,56),max(6,0,4,32,45,533));
return 0;
}
DSP下面printf串口输出的实现       前段时间在一个DSP上做音视频解码,没有操作系统 ,调试的时候又不能用仿真器狂打断点,不然同步逻辑跟时间相关不好调试,逼得自己写了个串口printf,现在把它整理出来。原来觉得printf的可变参数多神秘,这下算又搞清楚一些了。#define swap(x, y) {         asm( "swap %1, %0"       : "+r" (*(x)), "+r" (* 阅读详情

相关推荐

深入理解printf函数的实现

先看glibc中printf函数的实现源码: ​#include <ansidecl.h> #include <stdarg.h> #include <stdio.h> /* Write formatted output to stdout from the format string FORMAT. */ /* VARARGS1 */ int prin...

c1204611687的博客 1万+

自己用C语言实现printf

本文参考:https://www.pianshen.com/article/35981882012/ 前提的头文件: //================================================================================================== typedef char* va_list; #define _INTSIZEOF1(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(i

qq_30860901的博客 2211

[金仓数据库V8R6]更换授权

上传授权,使用mv license_xxx_0.dat /home/kingbase/KingbaseES/license.dat替换每个授权,并使用ls -l license.dat确保替换前后属主和权限不变。根据步骤1的数据目录位置,执行/home/kingbase/KingbaseES/Server/bin/sys_ctl -D /home/kingbase/KingbaseES/data reload使新的授权文件生效。该参数意义为授权剩余有效天数,查询结果为新授权中有效期间的天数即为替换成功。

l04050924的博客 3706

用C语言写自己的printf函数

第一部分分析 首先看一下printf函数的原型声明: int __cdecl printf(const char * __restrict__ _Format,...);   上面这个是我在Dev-C++上复制过来的函数声明,其实无论是哪个版本声明部分作用都是一样的。 接一下我们看一下printf的返回值是什么玩意?   上图就是我自己实际测试得出来的一个结果。 6621! ...

To_run_away的博客 3万+

【ros2】从零认识URDF:机器人模型描述的“说明书”

visual>子节点只负责机器人在RViz2中的可视化效果,不参与任何物理计算,就像给零件“穿衣服”。<origin>:定义零件的局部坐标系相对于连杆原点的位姿,参数xyz表示平移(单位:米),rpy表示旋转(单位:弧度,绕x→y→z轴旋转);<geometry>:定义零件的几何形状,支持四种基础形状+外部3D模型;<material>:定义零件的颜色,需要提前定义材质。常见几何形状示例-- 连杆名称:base_link(底座),全局唯一 --> < visual > <!

EnHengNa的博客 1609

基于车辆运动学的规划算法 - Hybrid A*/ State Lattice Planning (前置知识:自行车模型、Dubins、Reeds Shepp、多项式曲线、螺旋曲线)

本文将按顺序讲解基于车辆运动学的规划算法,以及需要的前置知识:自行车模型、Dubins和Reeds Shepp曲线、多项式曲线和螺旋曲线

BigDavid123的博客 4858

自己实现的简单Printf函数

#include #include void PrintStr(const char *pStr) { while(*pStr != '\0')// 1.*pStr != '\0' { putchar(*pStr); pStr++; } } void MyPrintf(const char *pFormat, ...) { va_list args; va_s

自由理想的足迹 1428

通讯软件019——分分钟学会Prosys OPC UA Server配置

本文介绍如何配置Prosys OPC UA Simulation Server,通过本文可以对OPC UA的基本概念有所了解,掌握OPC UA的本质。

zhouwl72的博客 4292

自己实现的printf函数

根据上一篇中的  《可变参数列表(非格式化控制)》,今天继续学习: 自己写一个函数实现printf功能。 1.自己实现的printf函数 #include &lt;stdio.h&gt; #include &lt;stdarg.h&gt; void my_printf(const char *str,...) { va_list var; char c = 0; unsigned ...

行者三个石的博客 3039

电路保护设计实战:保险丝选型、I²t计算与多级协同方案

电路保护是电子系统可靠性的基石,其核心原理是在发生过流或过压等异常情况时,及时切断故障回路,防止能量持续灌入导致元器件损坏甚至安全事故。从技术价值看,优秀的保护设计能显著提升产品寿命、安全性和市场口碑,尤其在快充、电动汽车等高功率密度应用中,保护器件已成为不可或缺的安全屏障。在众多保护器件中,保险丝作为最基础的过流保护元件,其正确选型与应用直接关系到保护效果。本文聚焦于保险丝选型的实战细节,深入解析了I²t(电流平方时间积)这一关键参数,它定义了熔断所需的能量阈值,是匹配被保护半导体器件耐受能力、实现有效保

weixin_42662171的博客 308

古风仙侠少女三视图提示词,一键生成角色设定

3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。平视正交视角,统一光照,纯白背景,无文字无水印,避免强透视和动态变形。3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。少女,古风仙侠少女气质,灵动、温婉、仙气。

欢迎来到鸾姝淡月博客,专为程序员和编程爱好者打造的技术博客。我们分享编程语言趋势、开发工具、编程哲学、职业发展等,助你掌握新技术,优化技能,激发编程热情。 1020

自己动手写printf -- 库函数printf的实现

大概去年夏天的时候,在《C和指针》里面一个练习题要求实现一个很简单的不包含打印浮点数功能的printf函数。但是很好奇,于是一直纠结下去,结果就是知道了printf的实现,自己也写了一个简单的。或许是夏天的原因吧,那时候暑假没回去,凌晨四点兴奋到不能睡觉。那时候刚开始写blog。没想整理一下,只是简单的把最重要的实现"工具"贴了一个blog在 http://blog.youkuaiyun.com/cinmy

Jason Leaster | Rebuilding the tower of babel 1万+

系统架构设计(以飞控系统、航电系统、机电管理系统、电子电气架构为例)

系统架构涉及对系统的结构和行为进行高层次的描述。它包括系统的组成部分、这些部分之间的关系、与外部环境的交互方式,以及满足特定功能和非功能性需求的方法。系统架构定义了系统的总体设计蓝图,指导系统的开发、集成、部署和维护。系统架构设计的目的是通过合理的结构和设计原则,确保系统能够高效、稳定、安全地运行,满足当前和未来的需求,同时具备良好的维护性、扩展性和可持续性。良好的系统架构不仅能够提高系统的开发效率和运行性能,还能降低长期的维护和运营成本,支持系统的持续演进和技术更新。

qq_41854911的博客 3051

printf 简单实现

stdarg.h>是 C 标准库中的一个头文件,提供了一组宏,用于访问可变数量的参数。stdarg.h头文件定义了一个变量类型va_list和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。可变参数的函数通在参数列表的末尾是使用省略号...定义的。序号宏 & 描述1这个宏初始化 ap 变量,它与 va_arg 和 va_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,即省略号之前的参数。2。

芯片驱动开发实践 1121

Linux加密框架中的算法和算法模式(1)——算法介绍

算法和算法模式

yangguoyu8023的博客 2031

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

printf函数实现

序:       在嵌入式系统中,串口常用来辅助调试输出一些调试信息。所以有必要实现串口的格式化输出功能,这可以由3种方法实现(就我目所知).1)使用系统库函数printf(),这就需要重载输入,输出函数int fputc(int ch, FILE *f);int fgetc(FILE *f).2)使用sprintf()函数将数据格式化到数组,然后将数组输出.也可以使用vsprintf

gilbert0571的专栏 1929

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

printf实现

printf是c语言下非常常用的一个函数,几乎每个程序员在一开始第一个学的函数应该就是printf吧! 今天来实现一个printf函数,虽然printf是我们常用的函数,但实现起来其实还是蛮考基础的! 下面一步一步带大家实现: 一. 编写一个入口函数并导入标准io头文件 #include&lt;stdin.h&gt; int main(){ } 二. printf函数准备工作 前言...

17岁boy的博客 5635

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

printf函数的实现

printf函数的实现 自己之前做了一个用于串口发送数据的函数,觉得调用的时候有点麻烦,于是想着自己用printf函数去实现用于串口发送数据。在csdn上找了一些,发现没有自己想要的,有也是有一些错误的,于是,我在他们的基础上做了一个printf函数,包教包会。 还是老习惯,先来看看本片文章的思维导图。 1.堆栈以及函数调用的基础知识 1.堆栈 这里对堆栈的定义以及一些性质我就不过多讲解了,我只将和printf函数有关的知识,栈又叫堆栈,它的生长方向既可以向上生长,也可以向下生长向上生长的最主要的代表就是

qq_46359697的博客 3136

模拟实现printf函数

标题 ##模拟实现printf函数,可完成下面的功能 // //能完成下面函数的调用。 //print(“s ccc d.\n”,”hello”,’b’,’i’,’t’,100); //函数原型: //print(char *format, …) // 1.认识一下printf: 在构建printf()函数之前,需要简单分析printf(),在帮助文档中可以看到,pri...

HHA的博客 392

嵌入式 printf的实现

在嵌入式中,经常需要用到printf来调试程序 标准库函数的默认输出设备是显示器,要实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数. printf输出到串口,需要将fputc里面的输出指向串口(重定向) 因此,实现printf就需要重定向相关的函数。有的时候,我们想自己实现printf的功能,不妨自己写一个 #include <stdarg.h> ...

weixin_30417487的博客 190

printf简单实现版本

这是stdio.h包中printf函数的一个简单实现代码,只针对%d,%s,%c作为处理。有兴趣的朋友可以将其继续扩展,也可以去寻找linux内核中的printf函数源代码加以研究。 实现代码主要用到了va_list,关于va_list方面的内容可参见点击打开链接 代码: #include #include #include int myprintf(const char *fm

半仙姜 5353

在lua中实现printf

本文讨论如何在lua中实现printf。学习的目的是为了进一步了解C函数和lua之间利用栈传值的过程。在lua中printf的功能等于string.format + io.wirte,前者用于格式化字符串,后者用于输出。文中的代码来自于lua官网。 用lua代码实现为: printf= function(s, ...)     return io.write(s:format(...))

语言观止 7646

printf 实现原理

一: 实例源码 printf 实现原理: printf 函数肯定有一个格式串,例子中为 "string = %s xx = %s , hex = %d \n",printf的实现是依据该格式串,智能的解析 % 后面的字符,假如第一个% 后面是s 代表格式串后面的第一个参数代表一个地址,如果是d则代表是一个十进制。 以此类推。 所以要特别注意格式串的编写,如果多写了(printf("%

aobai219的专栏 3581
上一篇: linux进程间通信的几种机制的比较及适用场合
下一篇: while与for执行效率对比
zhouxf_cn
博客等级 码龄14年 54粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值