STM32基础知识4-va_list原理及用法

发布者:快乐飞翔最新更新时间:2019-09-05 来源: eefocus关键字:STM32  基础知识  va_list 手机看文章 扫描二维码
随时随地手机看文章

VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。


下面是va_list的用法示例 :

#include  

int AveInt(int,...);

 void main()

{

       printf("%d/t",AveInt(2,2,3));

       printf("%d/t",AveInt(4,2,4,6,8));

       return;

}

int AveInt(int v,...)

{

       int ReturnValue=0;

       int i=v;

       va_list ap ;

       va_start(ap,v);

       while(i>0)

       {

              ReturnValue+=va_arg(ap,int) ;

              i--;

       }

       va_end(ap); 

       return ReturnValue/=v;

}

VA_LIST的用法:       

(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;

(2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;

(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);

(4)最后用VA_END宏结束可变参数的获取。


上面是va_list的具体用法,下面讲解一下va_list各个语句含义(如上示例黑体部分)和va_list的实现。


可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义 :

          typedef char * va_list;     // TC中定义为void*
          #define _INTSIZEOF(n)    ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统
          #define va_start(ap,v)    ( ap = (va_list)&v + _INTSIZEOF(v) )     //ap指向第一个变参的位置,即将第一个变参的地址赋予ap
          #define va_arg(ap,t)       ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   /*获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容*/
          #define va_end(ap) ( ap = (va_list)0 )   //清空va_list,即结束变参的获取


C语言的函数形参是从右向左压入堆栈的,以保证栈顶是第一个参数,而且x86平台内存分配顺序是从高地址到低地址。因此似函数AVEInt(int var1,int var2,...,int varN)内存分配大致上是这样的:(可变参数在中间)
栈区:
|栈顶             低地址
|第一个参数var1                  <-- &v
|第二个参数var2                  <-- va_start(ap,v)后ap指向地址       
|...
|函数的最后varN
|...
|函数的返回地址
|...
|栈底    高地址

va_list ap ;  定义一个va_list变量ap 
va_start(ap,v) ;执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即 ap指向第一个可变参数在堆栈的地址。
va_arg(ap,t) , ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。 ap+= sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针,这正是第一个可变参数在堆栈里的地址。然后 用*取得这个地址的内容。

va_end(ap) ; 清空va_list ap。

使用VA_LIST应该注意的问题: 

(1)因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型. 也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.

(2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。


(3)由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。


本文参考如下文章:

http://hi.baidu.com/kang_liang/blog/item/168c9059a9a1ca2d2934f05f.html

http://fenge.bokee.com/195016.html

http://blog.csdn.net/homer1984/archive/2009/02/02/3859036.aspx


关键字:STM32  基础知识  va_list 引用地址:STM32基础知识4-va_list原理及用法

上一篇:STM32F103程序设计-5-控制引脚高低电平的实现
下一篇:STM32基础知识2-分享PWM输入模式捕捉4路PWM波形的周期和占空比

推荐阅读最新更新时间:2024-11-11 03:17

STM32 模块篇-温湿度传感器模块(DHT11)实验
5.03 温湿度传感器模块实验 5.03.1 概述 DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8 位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11 传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP 内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20 米以上,使其成为各类应用甚至最为
[单片机]
<font color='red'>STM32</font> 模块篇-温湿度传感器模块(DHT11)实验
浅谈你不知道的STM32知识
STM32是一种功能比较强大的32位单片机,广泛应用于各种嵌入式设备中,由于它的普及性及丰富的资源,受到广大嵌入式开发者的喜欢,但要想学好用好STM32也并非易事,毕竟,相比8位、16位产品,STM32要复杂得多。 STM32的时钟 众所周知STM32有5个时钟源HSI、HSE、LSI、LSE、PLL,其实它只有四个,因为从下图中可以看到PLL都是由HSI或HSE提供的。 其中,高速时钟(HSE和HSI)提供给芯片主体的主时钟.低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用,图中可以看出高速时钟也可以提供给RTC。内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默
[单片机]
浅谈你不知道的<font color='red'>STM32</font>知识
STM32学习笔记一一UCOSII(1)
1.简介 UCOSII 是一个可以基于 ROM 运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。 1.1 UCOSII 体系结构图 UCOSII 的移植,我们只需要修改: os_cpu.h、 os_cpu_a.asm 和 os_cpu.c等三个文件。 os_cpu.h: 进行数据类型的定义,以及处理器相关代码和几个函数原型; os_cpu_a.asm:是移植过程中需要汇编完成的一些函数,主要就是任务切换函数; os_cpu.c:定义一些用户 HOOK 函数。 定时器的作用:为 UCOSII 提供系统时钟节拍,
[单片机]
<font color='red'>STM32</font>学习笔记一一UCOSII(1)
[stm32] 利用uc-gui封装画图和画线函数移植51上的模拟动画
_ :这里的动画是黄色矩形区域中一个模仿俯视图的起重机运作动画,一个是模仿主视图的吊钩的运动。通过改变初始Init函数中的数据b_x,b_y实现矩形区域的移动。当实时采集时要首先根据起重机的实际情况改变比例,当传感器传来数据时就相当于这里的run函数,只要把传感器数据接收函数和相关函数结合即可。 _ :main code: 1 #include stdlib.h 2 #include GUI.H 3 /* 4 颜色 5 */ 6 #define Black 0x000000 7 #define White 0xFFFFFF 8 #define Blue 0xFF0000 9 #define R
[单片机]
STM32 Bootloader 跳转
1. Bootload #define ApplicationAddress 0x08003000 2. App 修改
[单片机]
<font color='red'>STM32</font> Bootloader 跳转
STM32 DS18B20代码详解
DS18B20是最常用来学习某一个新的开发工具的,程序都是大同小异,主要是要注意时序中的延时要准确,指令要正确,这里记录一下! ------------------第一部分是--------ds18b20.h---------------------- #ifndef __DS18B20_H #define __DS18B20_H #include stm32f10x.h #include bsp_SysTick.h //精确延时函数头文件----参考http://blog.csdn.net/xuxuechen/article/details/40783209这个看一下 #define HIGH 1 #define
[单片机]
<font color='red'>STM32</font> DS18B20代码详解
STM32 DS18B20 代码详解 学习总结
DS18B20是最常用来学习某一个新的开发工具的,程序都是大同小异,主要是要注意时序中的延时要准确,指令要正确,这里记录一下! ------------------第一部分是--------ds18b20.h---------------------- #ifndef __DS18B20_H #define __DS18B20_H #include stm32f10x.h #include bsp_SysTick.h //精确延时函数头文件----参考http://blog.csdn.net/xuxuechen/article/details/40783209这个看一下 #define HIGH 1 #define LOW
[单片机]
<font color='red'>STM32</font> DS18B20 代码详解 学习总结
STM32之如何封装自己的lib库
似乎stm32刚开始出来的时候就是用的l.ib库,后来就开源了。 网上看了一些网友做的lib库,自己也弄了一下,其实也挺简单。 以下封装lib库都是基于stm32的3.0固件库 1、打开一个工程,将除固件库以外的其他文件删掉,如下: 2、在option下进行设置,如下: 来个特写: 3、将编译好的lib库拷贝到另外一个工程,添加进去,并将工程下库的c文件全部删除(h文件必须保留) 4、编译,如下: 注意,只需将stm32_lib_30.lib文件add进去就可以了,没必要在主文件里面include之类的。 否则编译的时候就会出错(重复包含),如下:
[单片机]
<font color='red'>STM32</font>之如何封装自己的lib库
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved