精华内容
下载资源
问答
  • ARM希望把中断处理函数也做成C函数的形式一般C函数的处理过程类似了。 进入中断前,首先要把(R0-R3,R12,LR,PSR)保存起来,然后在中断结束后恢复它们。 这一切都是通过硬件完成的。 但是,中断的返回地址并没有像...

    https://www.jianshu.com/p/52841b514868

    ARM希望把中断处理函数也做成C函数的形式,一般C函数的处理过程类似了。
    进入中断前,首先要把(R0-R3,R12,LR,PSR)保存起来,然后在中断结束后恢复它们。
    这一切都是通过硬件完成的。
    但是,中断的返回地址并没有像一般的C函数调用一样存储在LR中。(即LR放的不是前一个的PC,在中断发生时,当前的PC被存在了栈中,而不是LR中,LR被赋予了 新的功能;同时LR的值还是要被存入栈的,因为该函数也是要返回它自己的调用函数的)也就是说,中断过程中不但要像一般的C函数调用一样保存(R0-R3,R12,LR,PSR),还要保存中断返回地址(return address)。
    中断的硬件机制会把EXC_RETURN放进LR,在中断返回时触发中断返回,而不是一般的C函数返回。

    EXC_RETURN

    如上文所说,LR在进入中断后通过硬件更新为EXC_RETURN。
    EXC_RETURN位定义

    EXC_RETURN为中断返回提供了更多的必要信息,如上表所示。

    1. bit4,表明了压入的是8个字,还是26个字。因为带浮点运算单元和不带浮点运算单元是有区别的
    2. bit3,表明是返回到Thread模式还是Handler模式。也就是该中断之前是从线程模式进入的,还是从中断中进入的(中断嵌套)。
    3. bit2 返回到哪个栈,是程序栈(Process Stack)还是主栈(Main Stack)。

    在这里插入图片描述
    图8.2是一个不带浮点运算的8字,中断的栈帧状态。
    其中并没有包含R4-R11,和EXC_RETURN。
    这是为什么呢?它说的是栈帧,也就值中断发生后硬件自动完成的压栈,而R4-R11,和EXC_RETURN是需要手动保存的状态。EXC_RETURN值是放在LR寄存器中的,如果是中断嵌套,则return address 应该就是被嵌套的中断的EXC_RETURN即LR的值

    在这里插入图片描述

    展开全文
  • 一个函数调用另一个函数的时候,就可以把被调用函数以函数指针的形式作为参数传入 2、回调函数callback使用的技术就是函数指针: 回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。为此,你...
  • UCOS III学习(五)

    2020-04-15 22:01:26
    1、中断函数的一般形式 中断:应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的任务的过程叫做中断。 UCOSIII中中断服务函数一般格式如下 void USART1_IRQHandler(void) { OSIntEnter(); //...

    一、UCOS III中断管理

    1、中断函数的一般形式

    中断:应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的任务的过程叫做中断。
    UCOSIII中中断服务函数一般格式如下

    void USART1_IRQHandler(void)
    {
           OSIntEnter();			//中断进入函数
          //中断服务程序
          OSIntExit();				//中断退出函数
    }  
    

    中断进入函数的主要内容就是中断计数,对于绝大多数单片机都支持终端的嵌套,允许高优先级中断打断低优先级中断,这一关系同样适用于UCOS III ,在UCOS III中使用OSIntNestingCtr变量来保存中断嵌套层数,每进入一次中断就会进入一次中断进入函数就会对OSIntNestingCtr加1

    void  OSIntEnter (void)
    {
          if (OSRunning != OS_STATE_OS_RUNNING) {
    	  return
          }
          if (OSIntNestingCtr >= (OS_NESTING_CTR)250u) {
                return; 
          }
         OSIntNestingCtr++;
    }
    

    与之对应的中断退出函数,会在每次退出中断前被调用,然后对OSIntNestingCtr进行减1操作。

    2、中函数中的信号量与消息发布

    中断函数中若出现了信号发布或者消息发布函数,UCOSIII可以选择采取两种处理方式:1直接发布2延迟发布,两种模式的选择则是通过宏OS_CFG_ISR_POST_DEFERRED_EN来选择,当宏OS_CFG_ISR_POST_DEFERRED_EN为0时,采取直接发布模式,反正宏OS_CFG_ISR_POST_DEFERRED_EN为1时,采取延迟发布模式。
    两种模式的区别:当处于直接模式下,系统进入临界态是通过关闭中断的方式来进行临界段代码的保护,当系统处于 延迟模式下时,系统采取锁定任务调度器的方式进行临界段的代码保护。
    要说明的是:无论哪一种模式下中断服务程序都需要执行完毕,才能进行任务的调度,此前因为中断发生而被打断的任务会进入就绪状态,等待中断指向完毕再进行一次任务调度。
    1、直接发布
    在这里插入图片描述
    直接发布模式下,信号发布与消息发布都会直接被执行,待中断执行完毕后进行任务调度,若原来被打断的任务的优先级为最高优先级,则继续执行该任务,若有因为得到中断中发布的信号量或消息而进入就绪状态且该任务的优先级更高,则运行该任务。

    2,延迟发布
    在这里插入图片描述
    延迟发布比直接发布稍显复杂,他不会在中断服务程序内直接执行发布消息或信号量的函数,而是将这些函数和相应的参数写入中断队列并使中断队列处理函数进入就绪状态,当中断服务程序运行结束,由于就绪状态下中断队列处理函数的优先级为0一定是最高的,因此会先执行中断处理函数,将会先锁定任务调度器,待所有的消息发布函数和信号量发布函数执行完毕,重新进行任务调度。若出现比被中断打断任务优先级更高的任务则执行新任务,若没有则继续执行原任务。

    二、UCOSIII 时间管理

    1、任务延迟

    UCOSIII中的任务是一个无限循环并且还是一个抢占式内核,为了使高优先级的任务不至于独占CPU,可以给其他优先级较低任务获取CPU使用权的机会,UCOSIII中除空闲任务外的所有任务必须在合适的位置调用系统提供的延时函数,让当前的任务暂停运行一段时间并进行一个任务切换。延时函数有两种,OSTimeDly()和OSTimeDlyHMSM()。
    OSTimeDly()函数有三种工作模式:相对模式、周期模式和绝对模式。
    OSTimeDlyHMSM()函数仅在相对模式下工作。

    void  OSTimeDly (OS_TICK   dly,					//延时时间片数
                     OS_OPT    opt,					//延迟选项
                     OS_ERR   *p_err)				//保存报错信息
    

    opt选项
    OS_OPT_TIME_DLY 相对模式
    OS_OPT_TIME_TIMEOUT 与OS_OPT_TIME_DLY相同
    OS_OPT_TIME_MATCH 绝对模式
    OS_OPT_TIME_PERIODIC 周期模式

    void  OSTimeDlyHMSM (CPU_INT16U   hours,				//以小时为单位
                         CPU_INT16U   minutes,				//以分钟为单位
                         CPU_INT16U   seconds,				//以秒为单位
                         CPU_INT32U   milli,				//以毫秒为单位
                         OS_OPT       opt,					//延迟选项
                         OS_ERR      *p_err)				//返回报错信息	
    

    opt选项
    包含OSTimeDly()函数的四个选项的基础上,新增两个选项,OS_OPT_TIME_HMSM_STRICT选项下小时计数范围0~99,分钟计数范围0到59,秒范围0到59,毫秒范围0到999.
    OS_OPT_TIME_HMSM_NON_STRICT选项下,小时范围0到999,分钟范围0到9999,秒范围0到65535,毫秒范围0到4294967259.

    2、取消延迟函数

    延时任务任务可通过在其他任务中调用函数OSTimeDlyResume()取消延时而进入就绪状态,此函数最后会引发一次任务调度。

    3、获取和设置系统时间:

    UCOSIII定义了一个CPU_INT32U类型的全局变量OSTickCtr来记录系统时钟节拍数,在调用OSInit()时被初始化为0,以后每发生1个时钟节拍,OSTickCtr加1。
    OSTimeSet()允许用户改变当前时钟节拍计数器的值,慎用!!!!!
    OSTimeGet()用来获取动迁时钟节拍计数器的值。

    展开全文
  • fzero 求单变量非线性函数的零点 G g gamma 函数 gammainc 不完全 函数 gammaln 函数的对数 gca 获得当前轴句柄 gcbo 获得正执行"回调"的对象句柄 gcf 获得当前图对象句柄 gco 获得当前对象句柄 ...
  • SylixOS 中断响应时间测试

    千次阅读 2017-05-09 16:52:48
    在一些情况下,对于一些紧急的中断任务,系统需要为其提供稳定可靠的中断响应时间,但一般的中断服务函数,它响应时间可能会受到其他中断向量影响,延迟响应。在SylixOS中有两种解方案。 1.提高该中断向量...

    1.应用场景

    在一些情况下,对于一些紧急的中断任务,系统需要为其提供稳定可靠的中断响应时间,但一般的中断服务函数,它的响应时间可能会受到其他中断向量的影响,延迟响应。在SylixOS中有两种解方案。

    1.提高该中断向量优先级,打开中断嵌套来确保紧急中断的响应时间。

    2.对于多核处理器,可以采用中断绑核的形式,即将紧急中断绑定到某一核上,该核只处理紧急任务。

    下面通过测试正常情况下、中断嵌套情况下、中断绑核情况下的中断响应时间,来对比采用上述两种方式的优点。

    2.中断响应时间测试方案

    使用示波器自带的1KHZ输出方波作为CPU的中断源,下降沿中断,在中断服务里改变CPU引脚输出电平并捕获。中断响应时间为1KHZ方波由从高电平上升到0.7*VCC开始到CPU输出低电平的时间差。具体测试方案见PPT支持SMP的大型实时操作系统(IDE目录下的ppt目录里)。硬件连接图如图2- 1所示。

            图2- 1 中断响应时间测试硬件连接

    测试以zynq7000开发板为例,zynq7000处理器为双核处理器,中断控制器为GIC pl350,它支持中断嵌套和中断绑核。测试程序如程序清单2- 1所示。

    程序清单2- 1 中断响应时间测试代码

    /*********************************************************************************************************

    * 函数名称: __doIsr

    ** 功能描述: 中断服务函数

    ** 输 入 : VOID

    ** 输 出 :

    ** 全局变量:

    ** 调用模块:

    *********************************************************************************************************/

    static irqreturn_t __doIsr (PVOID pvArg, ULONG iVector)

    {

    API_GpioClearIrq(GPIO_NUM); /* 清中断 */

    writel(readl(0xE000A204) | (1 << 0), 0xE000A204); /* 11号脚,设置为出输出 */

    writel(readl(0xE000A208) | (1 << 0), 0xE000A208); /* 11号脚,设置为出输出使能 */

    writel(readl(0xE000A200) | (1 << 0), 0xE000A000); /* 11号脚,设置为低电平 */

    bspDelayUs(1); /* 延迟1us */

    writel(readl(0xE000A200) | (0 << 0), 0xE000A000); /* 11号脚,设置为高电平 */

    return LW_IRQ_HANDLED;

    }

    /*********************************************************************************************************

    ** 函数名称: module_init

    ** 功能描述: 模块加载函数

    ** 输 入 : VOID

    ** 输 出 : 0 表示成功,非0表示失败

    ** 全局变量:

    ** 调用模块:

    *********************************************************************************************************/

    int module_init (void)

    {

    system("shfile /etc/thread_affinity.sh");

         LW_CLASS_CPUSET cpuset;

          LW_CPU_ZERO(&cpuset);

    LW_CPU_SET(1, &cpuset);

      API_GpioRequestOne(GPIO_NUM, LW_GPIOF_IN, "intIn");                     /* 下降沿触发中断 */

    LW_IRQ = API_GpioSetupIrq(GPIO_NUM, LW_FALSE, 0);

    API_InterVectorConnect(LW_IRQ,                                                              /* 安装操作系统中断向量表 */

    (PINT_SVR_ROUTINE)__doIsr,

    (PVOID)LW_NULL,

    "isr");

     

    API_InterSetTarget(LW_IRQ, sizeof(LW_CLASS_CPUSET), &cpuset);     /* 设置中断目标CPU1 */ 

    API_InterVectorEnable(LW_IRQ);                                                                /* 中断使能 */

    return 0;

    }

    3.正常情况下中断响应时间测试

    在不开中断嵌套和中断不绑核的情况下,用上述方案进行测试。在不加负载的情况下,其中断响应时间如图3- 1所示。

    图3- 1 无负载下中断响应时间测试

    由图3- 1可以看出其中断响应时间大概为3.5us,并且实际测试时波动较大,中断响应时间不稳定。在加负载的情况下(内存拷贝负载和CPU负载),其中断响应时间测试结果如下图3- 2所示。

    图3- 2 加负载下中断响应时间测试

    由图3- 2可以看出,加负载后,内存拷贝等任务可能会产生中断,从而影响测试中断向量的中断响应时间,测试中断向量的中断响应时间已经达到7us,并且实际响应时间很不稳定,有时可能出现十几微妙的情况。对于紧急任务,这种不稳定情况是不允许出现的。

    4.中断嵌套下中断响应时间测试

    在SylixOS下,可以采用中断嵌套的方式加快高优先级中断向量的响应时间,将测试中断向量的优先级设置为最高,并且打开中断嵌套,测试中断响应时间,图4- 1可以看出各个中断向量分布在不同的CPU核上。

    图4- 1

    如图4- 2所示,测试中断向量的中断响应时间为3.5us,并且实际响应时没有太大波动(响应时间稳定在3.5us)。

    图4- 2 中断嵌套下无压力中断响应时间测试

    如图4- 3所示是在加负载(CPU负载和内存拷贝负载)的情况下,测试中断向量的响应时间为5us左右,并且实际响应没有太大波动(响应时间稳定在5us)。

    图4- 3 中断嵌套下加负载中断响应时间测试

    由测试结果可以看出,中断嵌套可以提高中断的响应时间。

    在单核处理器下,只能通过中断嵌套来保证紧急中断的响应时间,但多级中断嵌套存在中断栈溢出的风险,用户在使用时需要注意这一点。

    大部分中断控制器为了防止中断栈溢出,一般会限制中断嵌套的层数,例如AIC支持最多8级中断嵌套,GIC支持最多7级中断嵌套。

    SylixOS中用户可以手动配置中断栈的大小,通过memory_cfg.h 下的LW_CFG_INT_STK_SIZE选项进行中断栈大小的设置,SylixOS默认中断栈大小为4K,用户可以根据自身情况进行配置,配置选项程序如程序清单4- 1所示。

    程序清单4- 1 中断栈大小配置

    /*********************************************************************************************************

    * 基本二进制大小单位定义

    *********************************************************************************************************/

    #ifndef LW_CFG_KB_SIZE

    #define LW_CFG_KB_SIZE (1024)

    #define LW_CFG_MB_SIZE (1024 * LW_CFG_KB_SIZE)

    #define LW_CFG_GB_SIZE (1024 * LW_CFG_MB_SIZE)

    #endif

     

    /*********************************************************************************************************

    * KERNEL THREAD & INTERUPT STACK

    * (不包括网络与其他扩展子系统相关线程)

    *********************************************************************************************************/

    #define LW_CFG_INT_STK_SIZE (4 * LW_CFG_KB_SIZE)/* 系统中断堆栈大小 (字节) */

    ... …

     

    5.中断绑核下中断响应时间测试

    开中断嵌套存在一定风险,在多核处理器下,中断控制器一般都支持中断绑核。可以采用中断绑核的方式提高紧急中断向量的响应时间,中断绑核程序如程序清单5- 1所示。

         程序清单5- 1 中断绑核设置

        LW_CLASS_CPUSET cpuset;

        LW_CPU_ZERO(&cpuset);

        LW_CPU_SET(1, &cpuset);

        API_GpioRequestOne(GPIO_NUM, LW_GPIOF_IN, "intIn"); /* 下降沿触发中断 */

        LW_IRQ = API_GpioSetupIrq(GPIO_NUM, LW_FALSE, 0);

        API_InterVectorConnect(LW_IRQ, /* 安装操作系统中断向量表 */

           (PINT_SVR_ROUTINE)__doIsr,

                     (PVOID)LW_NULL,

       "isr");

     

        API_InterSetTarget(LW_IRQ, sizeof(LW_CLASS_CPUSET), &cpuset); /* 设置中断目标CPU1 */

        API_InterVectorEnable(LW_IRQ); /* 中断使能 */

     

    程序运行后,如图5- 2所示可以看出,测试中断向量绑定到CPU 1,其他中断向量绑定到了CPU 0。

    图5- 2 中断绑核

    在不加负载的情况下,中断响应时间测试结果如图5- 3所示,可以看出中断响应时间在3us左右,并且实际测试时响应时间没有太大波动。

    图5- 3 绑核无负载中断响应时间测试结果

    在加负载(CPU负载和内存拷贝负载)的情况下,响应测试结果如图5- 4所示,响应时间为4us,并且实际测试时响应时间没有太大波动,负载对紧急任务的中断响应时间影响很小。

    图5- 4 绑核有负载中断响应时间测试结果

     

    根据上述测试结果可以看出,中断绑核也可以保证中断响应时间的稳定可靠

    展开全文
  • 将控制权转移给被调用函数:主调函数的执行被暂时中断,被调函数开始执行 尽管实参与形参存在对应关系,但是并没有规定实参的求值顺序。编译器能以任意可行的顺序对实参求值 任意两个形参都不能同名,形式参数...
    • 通过调用运算符()调用函数

    • 函数的调用完成两项工作:

      • 用实参初始化函数对应的形参
      • 将控制权转移给被调用函数:主调函数的执行被暂时中断,被调函数开始执行
    • 尽管实参与形参存在对应关系,但是并没有规定实参的求值顺序。编译器能以任意可行的顺序对实参求值

    • 任意两个形参都不能同名,形式参数名是可选的,但是由于我们无法使用未命名的形式参数,所以形式参数一般都应该有一个名字

    • 函数的返回类型不能是数组类型或者函数类型,但可以是指向数组或者函数的指针

    • 函数体必须是大括号包围的!

    • 普通局部变量对应的对象是自动对象:当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。

    • 局部静态对象:在程序的执行路径第一次经过对象定义时对它初始化,并且直到程序终止时才被销毁,在此期间即使对象所在的函数执行结束也不会对他有影响。如果局部静态变量没有显式的初始值,则执行值初始化(内置类型会初始化为0)

    • 函数只能定义一次,但是可以声名多次,唯一的区别是函数的声明不需要函数体,用一个分号替代即可(因此经常省略形式参数的名字,但是写上名字也有利于理解函数的功能),函数声明也称作函数原型

    • 形式参数初始化的机理和变量初始化一样

    • 拷贝大的类类型对象或者容器对象比较低效,甚至有的类型(包括IO类型在内)根本不支持拷贝操作。因此函数只能通过引用形式参数访问该类型的对象,如果函数无须改变引用形式参数的值,最好将其声明为常量引用

    • 熟悉C的程序员常常使用指针类型的形参访问函数外部的对象,在C++中最好还是使用引用类型的形式参数代替指针

    • 对于有可能是临时参数的形式参数,我们不应该使用引用(因为无法引用到常量上)

    • 函数重载要求同名函数的形式参数列表应该有明显的区别,因此如果仅仅是const的不同则不能进行重载(应该要求形式参数类型不同)

    • 形式参数的初始化方式和变量的初始化方式是一样的:我们可以使用非常量初始化一个底层const对象,但是反过来不行,同时一个普通的引用必须使用同类型的对象初始化(详细同第二章指针和引用部分)

    • 尽量使用常量引用

      • 给函数调用者传递正确的信息
      • 使用非常量引用会极大地限制函数所能接受的实际参数类型:我们不能把const对象、字面值对象或者需要类型转换的对象传递给普通的引用参数
    • 数组形式参数

      • 数组的两个特点:

        • 不允许拷贝数组
        • 使用数组时通常会将其转换成指针
      • 以下三种声明方式是等价的:

        void print(const int*);
        void print(const int[]);
        void print(const int[10]);
        
      • 因为我们不清楚数组的实际大小,因此在使用过程中必须通过一定的方式判断是否越界

        • 使用标记指定数组长度:例如C风格的字符串,最后一个一定是一个\0,我们可以判断是否为\0来判断是否到达末尾

        • 使用标准库规范:

          void print(const int *beg, const int *end) {
          	while(beg != end) {
          		cout << *beg++ << endl;
          	}
          }
          int arr[] = {0, 1, 2};
          print(begin(arr), end(arr));	//#include<iterator>
          
        • 显式传递一个表示数组大小的形式参数

        • 同常量引用,当函数不需要对数组元素执行读写操作的时候,数组形式参数应该是指向const的指针

      • 数组引用参数

        void print(int (&arr)[10]) {
        	for (auto item : arr) {
        		cout << item << endl;
        	}
        }
        

        对于数组的引用详细可以看第三章关于数组部分的笔记

      • 传递多维数组:C++语言中实际上没有真正的多维数组,所谓的多维数组其实是数组的数组。数组第二维(以及后面所有的维度)的大小都是数组类型的一部分,不能省略

        void print(int (*matrix)[10], int rowSize);
        void print(int matrix[][10], int rowSize);
        

        上面两种声明是完全等价的

    • 命令行选项可以通过两个(可选的)形式参数传递给main函数:

      int main(int argc, char *argv[]);
      int main(int argc, char **argv);
      
      • 第二个形式参数是一个数组,它的元素指向C风格字符串的指针,第一个形式参数argc表示数组中字符串的数量
      • argv第一个元素指向程序的名字或者一个空字符串,接下来的元素依次传递命令行提供的参数,最后一个指针的元素值保证为0
    • 为了编写能处理不同数量实际参数的函数,C++11标准提供了几种方法:

      • 如果函数的实际参数数量未知但是类型相同,我们可以使用initializer_list类型的形式参数(需要#include<initializer_list

        initializer_list<T> lst;	//默认初始化,T类型元素的空列表
        initializer_list<T> lst{a,b,c...};	//lst的元素是对应初始值的副本,列表中的元素是const
        lst2(lst);	 //等价与lst2 = lst ,赋值,不会拷贝列表中的元素,原始列表和副本共享元素
        lst.size()		//列表中的元素数量
        lst.begin()
        lst.end()
        

        initialzer_list对象中的元素永远是常量值

        void err_msg(initializer_list<string> il) 
        {
            for (auto beg = il.begin(); beg != il.end(); ++beg)
                cout << *beg << " ";	//也可以通过范围for循环访问65
            cout << endl;
        }
        err_msg({"A", "B", "C"});
        err_msg({"A", "B"});
        
      • 使用可变参数模板

    • 返回void的函数不要求非得有return语句,因为在这类函数的最后一句会隐式地执行return,一个返回类型是void的函数也能使用return expression,不过此时return语句的expression必须是另一个返回void的函数,强行令void函数返回其他类型将产生编译错误

    • 有返回值函数

      bool str_subrange(const string &str1, const string &str2) 
      {
          auto size = (str1.size() < str2.size()) ? str1.size() : str2.size();
          for (decltype(size) i = 0; i < size; ++i) {
              if (str1[i] != str2[i])
                  return false;
          }
          return true;
      }
      
    • 返回一个值的方式和初始化一个变量或形式参数的方式完全一样:返回的值用于初始化调用点的一个临时量,这个临时量就是函数调用的结果

    • 不要返回局部对象的引用或者指针:函数完成后,它所占用的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用(或指针)将指向不再有效的内存区域

    • 调用一个返回引用的函数得到左值,其他返回类型得到右值

    • C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化

    • 我们允许main函数没有return语句直接结束,如果控制到达了main函数的结尾处而且没有return语句,编译器将隐式地插入一条返回0的return语句。在cstdlib头文件中定义了两个预处理变量,我们用这两个变量分别表示成功与失败

      int main()
      {
      	if (some_failure) {
      		return EXIT_FAILURE;	
      	} else {
              return EXIT_SUCCESS;
          }
      }
      
    • 从语法上来说,想要定义一个返回数组的指针或引用的函数比较繁琐,但是使用类型别名可以简化这一任务

      typedef int arrT[10];	//using arrT = int[10];
      arrT* func(int i);		//返回一个指向含有10个整数的数组的指针
      int (*func(int i))[10];	//等价于上面的声明
      

      我们还可以使用尾置返回类型使得上面的声明变得清晰:

      auto func(int i) -> int(*)[10];
      

      如果我们知道函数返回的指针指向哪个(类别)的数组,我们还可以使用decltype关键字声明返回类型

      int arr[] = {0, 1, 2, 3, 4};
      decltype(arr) *arrPtr(int i) 
      {
      	return &arr;    
      }
      
    • 如果同一作用域内的几个函数名字相同但是形式参数列表(形式参数数量或形式参数类型)不同,我们称之为重载函数。main函数不能重载。需要注意的是,函数的重载和返回类型关系不大

    • 顶层const不影响传入函数的对象,一个拥有顶层const的形式参数无法和另一个没有顶层const的形式参数区分开来。但是底层const是会影响函数的重载的,当传入的对象是常量时,会选择带有底层cosnt的函数版本,如果传递一个非常量对象,编译器会优先选用非常量版本的函数

    • 最好只重载那些确实非常相似的操作

    • 我们也可以使用const_cast实现const到非const的转换:

      const string &func(const string &s1, const string &s2)
      {
          return s1.size() < s2.size() ? s1 : s2;
      }
      string &func(string &s1, string &s2)
      {
          return const_cast<string&>(func(const_cast<const string&>(s1), const_cast<const string&)(s2));
      }
      
    • 当调用重载函数时的结果:

      • 编译器找到一个与实际参数最佳匹配的函数
      • 找不到任何一个函数匹配,发出无匹配的错误
      • 有多于一个函数可以匹配,但是没一个都不是明显的最佳选择,此时也将发生错误,称为二义性调用
    • 如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。在不同的作用域中无法重载函数名

    • 一旦某个形式参数被赋予了默认值,它后面所有形式参数都必须有默认值。当设计含有默认实际参数的函数时,其中一项任务是合理设置形式参数顺序,尽量让不怎么使用默认值的形式参数出现在前面

    • 通常,应该在函数声明中指定默认实际参数,并将声明放在合适的头文件中。局部变量不能作为默认实际参数

    • 用作默认实际参数的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时。比如函数A某个默认实际参数的值是一个函数B调用的返回值,则该函数调用B会在A被调用的时候调用

      #include <iostream>
      
      using namespace std;
      
      string A = "global A";
      string B = "global B";
      
      const string &func()
      {
          return const_cast<const string&>(B);
      }
      
      int main() 
      {
          ios::sync_with_stdio(false);
          void test(const string &a = A, const string &b = func());
          string A = "local A";   //local varibale cannot be default value
          ::A += " has been changed";
          B = "local B";
          test();
          return 0;
      }
      
      void test(const string &a, const string &b)
      {
          cout << a << endl;
          cout << b << endl;
      }
      
      

      运行结果:

      global A has been changed
      local B
      
    • 将一些简单但需要多次重复的函数定义为内联函数的好处:

      • 有利于阅读理解
      • 可以被重复利用,使得代码简洁
      • 需要修改时只用修改一个地方
    • 在函数前面加上inline便可以将函数生命为内联函数。内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数

    • constexpr函数是指能够用于常量表达式的函数

      • 函数的返回类型以及所有形式参数的类型都必须是字面值类型,而且函数体中必须有且只有一条return语句
      • constexpr函数被隐式地指定为内联函数
      • 允许constexpr函数的返回值不是一个常量,如果参数非常量表达式导致最后返回值不是常量表达式则在需要常量的地方调用会报错
    • 内联(inline)函数和constexpr函数可以在程序中多次定义,但是多个定义必须一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中

    • 程序可以包含一些用于调试的代码,但是这些代码只在开发程序时使用。当应用程序编写完成准备发布时,要先屏蔽掉调试代码

      • assert (expr)预处理宏:首先对expr求值,如果表达式为假(0),assert输出信息并终止程序的执行,如果为真,则什么也不做
        • 需要头文件cassert,因为是供预处理器处理,所以无需提供using声明
        • assert宏常用于检查“不能发生”的条件,即程序的运行是建立在assert的条件成立的情况下
      • assert的行为依赖于一个名为NDEBUG的预处理变量的状态,如果定义了NDEBUG,则assert什么也不做。默认情况下没有定义NDEBUG,此时assert将执行检查。如果想要关闭assert检查:
        • 在程序开头加上#define NDEBUG
        • 或在编译的时候加上-D NDEBUG参数
      assert(word.size() >=  threshold);
      //等价写法:
      #ifndef NDEBUG
      if (word.size() < threshold)
          cerr << "Error: " << __FILE__
          	<< " : in function " << __func__
          	<< " at line " << __LINE__ << endl
          	<< "	Compiled on " << __DATE__
          	<< " at " << __TIME__ << endl
          	<< "	Word read was \"" << word
          	<< "\": Length too short" << endl;
      #endif
      
    • 函数匹配

      • 选定候选函数:
        • 与被调用函数同名
        • 其声明在调用点可见
      • 选定可行函数:
        • 形式参数和实际参数数量一直
        • 类型符合(相同或可以进行转换)
      • 寻找最佳匹配
        • 该函数的每个实际参数的匹配不劣于其他可行函数需要的匹配
        • 至少有一个实际参数的匹配优于其他可行函数提供的匹配
      • 如果最终确定了一个函数,则匹配成功,如果最后匹配出多个函数,则匹配失败,报告二义性错误
    • 为了确定最佳匹配,编译器将实际参数类型到形式参数类型的转换分成了几个等级:

      1. 精确匹配
        • 实际参数类型和形式参数类型相同
        • 实际参数从数组类型或函数类型转换成相应的指针类型
        • 向实际参数添加或者删除顶层const
      2. 通过const转换实现的匹配
      3. 通过类型提升实现的匹配(小整数类型会自动变成int,如果放不下再变成unsigned int
      4. 通过算数类型转换或指针转换实现的匹配
      5. 通过类类型转换实现的匹配
    • 想要声明一个指向函数的指针,只需要用指针替换函数名即可:

      bool func(const string &, const string &);
      bool (*pf)(const string &, const string &);
      

      当我们把函数名作为一个值使用时,该函数自动地转换成指针

      pf = func;
      //等价于
      pf = &func;	//&是可选的
      

      我们可以直接使用指向函数的指针调用该函数,无需提前解引用指针

      //等价的三种调用方法
      bool b1 = pf("A", "B");
      bool b2 = (*pf)("A", "B");
      bool b3 = func("A", "B");
      
    • 在指向不同函数类型的指针之间不存在转换规则,但是我们可以为函数指针赋一个nullptr或者0

    • 当我们使用重载函数为指针赋值时,上下文必须清晰地界定到底应该选用哪个函数

    • 我们可以定义函数指针作为形式参数

      void work(bool pf(const string &, const string &));	//看起来是函数类型,实际上会自动转换成指针
      //等价于
      void work(bool (*pf)(const string &, const string &));
      

      我们同样可以使用typedefdecltype简化操作

      typedef bool funcT(const string &, const string &);	//funcT是函数类型
      typedef decltype(func) funcT2;	//同上
      typedef bool (*funcTP)(const string &, const string &);	//funcTP是函数指针
      typedef decltype(func) *funcTP2;	//同上
      void work(funcT);	//同之前定义,函数类型会自动转换成指针类型
      void work(funcTP);	//同之前定义
      
    • 编译器不会自动地将函数返回类型当成对应的指针类型进行处理

      using F = int(int *, int);
      using FP = int(*)(int *, int);
      //以下四种方式是等价的
      FP f1(int);
      F *f1(int);
      int (*f1(int))(int *, int);
      auto f1(int) -> int (*)(int *, int);
      

      如果使用decltype指定返回函数指针类型记得decltype(func)如果func是一个函数则得到的是函数类型,还需要加上*

      decltype(func) *getFunc(const string &);
      
    展开全文
  • 通信中中断概率及代码表示

    千次阅读 热门讨论 2020-10-17 09:32:31
    设SNR的函数表达式为f(x),则中断概率可以表示为: 一般取r0=1或2 代码表示: close all; N=1000000;%仿真点数 x=randi([0,1],1,N); %产生随机信号 y=pskmod(x,2);%调用matlab自带psk调制函数
  • C语言屏幕函数

    2007-10-03 21:40:20
    在这之前,机器一直判断用户是否按了“Del”键,如果按了就进入ROM-BIOS中系统设置程序,将系统配置情况(如软、硬盘型号)以参数的形式存入CMOS RAM中,然后重新启动。 之后,自检程序将根据CMOS RAM中内容...
  • 程序一般都需要检查每个函数调用是否返回错误。...包裹函数的作用就是,完成实际的函数调用,并检查返回值,并在发生错误时终止进程。约定包裹函数名为实际函数名首字母大写形式。int Socket(int family,int
  • 可能会因为网络超时连接中断,或者程序故障抛出各种异常,在实际中我们一般要避免在程序流程中下载文件。如果是实在无法避免,用什么办法比较高效呢?或者说比较轻量级呢?本文就来说一说。学习时间最直接办法就是...
  • c/c++的编译器在处理c/c++语言的函数的时候,会在这个函数的开头与结尾加上很多栈操作,以支持程序调用,比如上边的代码就有可能被c/c++编译器处理成如下形式:(其中绿色为编译器自行加上的代码) pusha pushad...
  • 嵌入式程序启动代码,就是startup代码,一般用汇编编写,代码效率高,但和C++代码融合起来不太方便,例如,想直接在cpp文件定义中断函数就需要asm语句。 下面介绍一下avr芯片启动过程: avr启动分多个...
  • 系统调用就是操作系统提供的一些功能供程序员们调用,这些调用已经被封装成了C函数的形式,但是它们不是标准C的一部分。 一般应用程序运行在用户态(使用的是0至3G的内存),系统调用工作在内核态(使用的是3至4G的内存...
  • 系统调用就是操作系统提供的一些功能供程序员们调用,这些调用已经被封装成了C函数的形式,但是它们不是标准C的一部分。 一般应用程序运行在用户态(使用的是0~3G的内存),系统调用工作在内存态(使用的是3~4G的内存)...
  •  1 内存变量定义的一般形式  2 字节变量  3 字变量  4 双字变量  5 六字节变量  6 八字节变量  7 十字节变量  4.3 调整偏移量伪指令  1 偶对齐伪指令  2 对齐伪指令  3 调整偏移量伪指令  4 偏移量...
  •  1 内存变量定义的一般形式  2 字节变量  3 字变量  4 双字变量  5 六字节变量  6 八字节变量  7 十字节变量  4.3 调整偏移量伪指令  1 偶对齐伪指令  2 对齐伪指令  3 调整偏移量伪指令  4 偏移量...
  •  1 内存变量定义的一般形式  2 字节变量  3 字变量  4 双字变量  5 六字节变量  6 八字节变量  7 十字节变量  4.3 调整偏移量伪指令  1 偶对齐伪指令  2 对齐伪指令  3 调整偏移量伪指令  4 偏移量...
  •  1 内存变量定义的一般形式  2 字节变量  3 字变量  4 双字变量  5 六字节变量  6 八字节变量  7 十字节变量  4.3 调整偏移量伪指令  1 偶对齐伪指令  2 对齐伪指令  3 调整偏移量伪指令  4 偏移量...
  •  1 内存变量定义的一般形式  2 字节变量  3 字变量  4 双字变量  5 六字节变量  6 八字节变量  7 十字节变量  4.3 调整偏移量伪指令  1 偶对齐伪指令  2 对齐伪指令  3 调整偏移量伪指令  4 偏移量...
  • 你必须知道495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    4.12 我看到了用指针调用函数的不同语法形式。到底怎么回事? 4.13 通用指针类型是什么?当我把函数指针赋向void*类型的时候,编译通不过。 4.14 怎样在整型和指针之间进行转换?能否暂时把整数放入指针变量中...
  •  3.1.3 运行时函数的调用  3.2 用DDK编译环境编译驱动程序  3.2.1 编译版本  3.2.2 nmake工具  3.2.3 build工具  3.2.4 makefile文件  3.2.5 dirs文件  3.2.6 sources文件  3.2.7 makefile.inc文件...
  • 这种情况在汇编语言中表现形式一般是: 保存用到寄存器(保护现场) 处理终端 回复用到寄存器(恢复现场) 中断返回 Cortex-M处理器内核提供了一个用于中断管理嵌套向量中断控制器(NVIC),最多支持240个中断...
  • Microsoft Visual C++ 6.0实现串口通信

    千次阅读 2008-11-06 17:25:00
    当我们在Windows操作系统下开发串行通信程序时通常不得不面对许多复杂API函数,因为在Windows操作系统下不能直接对设备端口进行操作,也不能在系统级(Ring 3级别)使用任何DOS或BIOS中断,如要对端口进行编程则...
  • 《你必须知道495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    4.12 我看到了用指针调用函数的不同语法形式。到底怎么回事? 50 4.13 通用指针类型是什么?当我把函数指针赋向void *类型的时候,编译通不过。 51 4.14 怎样在整型和指针之间进行转换?能否暂时把整数放入指针...
  • 4.12 我看到了用指针调用函数的不同语法形式。到底怎么回事? 4.13 通用指针类型是什么?当我把函数指针赋向void *类型的时候,编译通不过。 4.14 怎样在整型和指针之间进行转换?能否暂时把整数放入指针变量中,...
  • 外因:即射频干扰,它是以空间电磁场的形式传递在机器内部导体(引线 或零件引脚)感生出相应干扰,可通过电磁屏蔽和合理布线/器件布局衰减 该类干扰;电源线或电源内部产生干扰,它是通过电源线或电源内...
  • 4.12 我看到了用指针调用函数的不同语法形式。到底怎么回事? 50 4.13 通用指针类型是什么?当我把函数指针赋向void *类型的时候,编译通不过。 51 4.14 怎样在整型和指针之间进行转换?能否暂时把整数放入指针...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 141
精华内容 56
关键字:

中断函数的一般形式