我们在STM32中常常使用printf来将信息打印至PC端控制台上。但是有这样一种函数,它可以把数据以我们指定的格式装进字符串中——sprinft和vsprintf。
⑴ sprintf函数使用的方式如下:
① 将多个参数以指定格式写入字符串
int a=1,b=2;
char s[10];
sprintf(s,"a=%d,b=%d",1,2);
puts(s);
输出结果:a=1,b=2
② 错误使用:在函数封装中使用
void Myprintf(const char* fmt,...) // 传递个数不定的参数
{
char s[10];
sprintf(s,fmt); // 出现错误
puts(s);
}
封装函数的使用:
int a=1,b=2;
Myprintf("a=%d,b=%d",a,b);
输出结果:
a=?,b=? // 不确定值
这是因为可变参数在入栈时如下列方式排列:
但是sprintf只接收了Param1作为自己的参数,这样的话,我们调用的封装函数等价如下:
int a=1,b=2;
char s[10];
sprintf(s,"a=%d,b=%d"); // 这里的a,b根本没有值
puts(s);
⑵ vsprintf粉墨登场了,它解决了sprintf无法读取可变参数的缺陷
我们要知道可变参数在栈中的排列是连续的,而且占用了一段栈的内存空间,我们首先要知道栈区中可变形参的排列顺序:
int func(int num,...)
{
......
}
其对应可变参数在栈区中的排列顺序是:
这里要注意:入栈的顺序是“先入后出-FILO”,因此这里的元素在栈中是倒序排列的,即栈区的末端地址就等于可变参数列表中首个参数的地址。
这里我们要将不定个数的参数传递给函数vsprintf函数,我们就必须要借助这N个参数(参数N~参数1)的地址来进行传参。我们一定要清楚:可变参数指的是参数1~N不包括第一个参数num,这个参数是已知的不算可变参数。
函数vsprintf原型如下:
// 函数功能:将函数地址->ParamEndAddr地址之间的所有参数以Format指定的格式转化为字符串进而赋给以StringFirstAddr为首地址的字符数组
// ParamEndAddr:参数列表中首个参数的地址(栈区中可变参数列表的末端地址)
// Format:指定转化为字符串的格式(详见:printf函数打印字符串的格式)
// StringFirstAddr:用于接受转换后字符串的字符型数组首地址
vpsrintf(char* StringFirstAddr, Format, ParamEndAddr);
这里我们要注意:函数在内存中的分布分为RAM和FLASH两大块,其中参数存在RAM中函数的执行内容存在FLASH中,这里的函数名就是参数列表所在栈区在RAM中的首地址,当我们提供一个RAM中参数列表所在栈区的末端地址,我们就可以将整个可变参数列表提取出来。
这里我们介绍几个函数:
程序如下:
void func(char *fmt, ...)
{
va_list ap; // 指向参数列表(栈区)中某个元素的指针类型
va_start(ap, fmt);
va_arg(ap, int);
va_end(va);
}
① 确定“栈区末端地址”的函数——va_start()
函数原型:va_start(va_list ap, 参数列表中的第一个元素)
函数功能:va_start(va_list ap, char* fmt)
函数的调用使得ap指针指向可变参数列表中的首个元素也是栈区中的最后一个元素地址。
② 根据栈区末端地址和参数类型提取可变参数列表中的指定元素——va_arg()
函数原型:va_arg(va_list ap, 元素的数据类型)
函数功能:va_arg(va_list ap, int)
va_arg()所做的就是根据ap指向的地址,和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。
③ 将指针置空(NULL)的函数——va_end()
函数原型:va_end(va_list ap)
函数功能:va_end()所做的就是让ap这个指针指向0。
函数vsprintf使用方式如下:
void Myprintf(const char* fmt,...)
{
char s[10];
va_list ap; // 定义指向栈区某个元素的指针
va_start(ap,fmt); // ap指向可变参数列表的首地址(栈区的末端元素地址)
vsprintf(s,fmt,ap); // 将函数首地址~ap之间的可变形参列表赋值给vsprintf函数形参列表
va_end(ap); // ap指针置空(NULL)
puts(s); // 打印字符串至屏幕上
}
但是,这里将一段封装函数Myprintf的参数列表赋值给vsprintf函数当作参数,如果我们操作单个可变参数列表中的元素,我们该如何做呢?
相较于前面一段一段的使用参数列表的元素的最大不同在于“我们需要指定输入参数的个数”,因为我们输入的参数的数据类型未知,个数未知,因此我们要知道我们每个输入参数的数据类型和输入参数的总个数才可以。
// 这里我们指定了可变形参个数为num个
float Average(int num,...)
{
int i=0;
float sum;
va_list valist;
va_start(valist,num); // 通过参数列表中收个元素(int num)找到可变参数列表中的收个元素地址并赋值给valist
for (i = 0; i < num; i++)
{
// 根据输入元素的数据类型在栈区中读取数据
sum+=(float)va_arg(valist,int); // 假设所有输入元素全文int类型的参数,当不断循环时不断改变valist指针指向元素,进而不断轮询访问参数列表中的所有元素
}
va_end(valist); // 置空(NULL)valist指针
return sum/(float)num;
}
注意:我们要使用这些函数必须要加载“#include
上一篇:基于战舰V3的MPU6050六轴陀螺仪实验
下一篇:独立看门狗IWDG详细解析
推荐阅读最新更新时间:2024-11-11 10:49
推荐帖子
- 怎样测试TMS320DM6446是不是坏了
- TMS320DM6446主板怎么回事采用TMS320DM6446主板测试找不到ip地址,灯亮的正常,不知是不是TMS320DM6446换了怎样测试TMS320DM6446是不是坏了
- 姚德军 DSP 与 ARM 处理器
- 【2024 DigiKey 创意大赛】智能垃圾桶开箱贴
- 元器件开箱贴:智能垃圾桶项目开箱情况 外观与尺寸:SeedSoM-STM32MP157C是一个小型的单板计算机,尺寸适中,外观精致,非常适合嵌入式项目。 功能特点: 处理能力:基于高性能的STM32MP157C芯片,具有强大的处理能力,适合处理复杂的数据和控制逻辑。 I/O接口:提供丰富的I/O接口,包括GPIO、SPI、I2C等,方便连接各种传感器和执行器。 操作系统支持:支持多种实时操作系统,如FreeRTOS,适合开发复杂的嵌入式应用。
- zfw152666 DigiKey得捷技术专区
- 请问在ce6平台下怎么测试短信和电话程序呢?
- 在WM6下,仿真器可以与CellularEmulator配合使用测试电话和短信,但wince6在仿真器环境下该怎么弄呢?请问在ce6平台下怎么测试短信和电话程序呢?我也要弄电话功能,你用的GSM是什么?我的是SIM300C!我还没硬件呢,只是在仿真器上研究,不知道有没有跟仿真器配合使用的工具引用2楼srz007的回复:我还没硬件呢,只是在仿真器上研究,不知道有没有跟仿真器配合使用的工具 没有配件,还是不要想啦!模拟器是一点可能也没有的。。。。有设备的话就LOG呗。呵呵
- goout 嵌入式系统
- 左手材料的理论起源与研究进展(转贴)
- 2001年美国加州大学制成一种新型异性材料并观察到其反常折射现象,引起学术界关注;2003年美国麻省理工大学实验证明了这种异性材料的可制造性,引起学术界、工业界和军方的高度重视;2003年美国《科学》杂志将这种奇异材料的研制纳入年度十大科学进展。这是科学界谓之的一种颠倒了物理学常规定律的“左手材料”,它在固体物理、材料科学、光学和应用电磁学领域内开始获得愈来愈广泛的青睐,其研究正呈现迅速发展之势,其反常特性则预示着材料世界前所未有的革命性时刻的到来。一、左手理论的起源“左手材料”研究所
- yh19782000 RF/无线
- 中国MCU市场厂商排名2012
- 2012年供应商排名 1、RenesasElectronics 2、FreescaleSemiconductor 3、MicrochipTechnology 4、STMicroelectronics 5、Atmel 6、InfineonTechnologies 7、NXP 8、TexasInstruments 9、Toshiba 10、Samsung中国MCU市场厂商排名2012other也占
- wangfuchong 单片机
- 【MicroPython】+家用气象仪
- 我的家用气像仪已经开工了,以后分享的内容都在会本贴分享。全程都开源!欢迎大家来指教,纯的菜鸟,所以希望大家帮忙指正。估对这python语言是完全零了解,所以是一边看着语法书一边写的,github.com上面的分享的驱动也挺多的,不过有些怪怪的,比如MicroPython5110的驱动不是太完善,没有提供简易字库供,需要自己建字库,另外也没有提供单字符与字符串输出API,所以就需要自己来写了,昨天简单的驱动一下LCD5110,今天去github.com别的树莓派上拷了个简易字库,建了一个
- strong161 MicroPython开源版块
设计资源 培训 开发板 精华推荐
- #第六届立创电赛#天气时钟语音助手
- 使用 NXP Semiconductors 的 FS32V234 的参考设计
- LTC3857EGN-1 高效双路 12V/5V 降压转换器的典型应用电路
- LT1634CCZ-2.5 超准确 ±4.096V 输出电压基准的典型应用
- SW3518 & SW3518S 方案
- HV7100DB1,+24V/+48V 风扇驱动器/控制器演示板
- LTC3525D-3.3 的典型应用 - 具有直通模式的 400mA 微功率同步升压型 DC/DC 转换器
- DAC通用电源板(搭载松下30W方牛)
- AD8604ARUZ-REEL 高端运算放大器电流监视器的典型应用
- LT1579CS8-3 5V 双电池备用稳压器电源的典型应用电路