08_goto和void分析

本文详细分析了goto语句的副作用及其对程序结构的影响,并对比了void在函数声明中的使用,包括func(void)与func()的区别,以及void修饰指针的应用。

goto和void分析

1、goto关键字副作用分析

goto语句会破坏程序的结构
程序示例:

#include <stdio.h>
#include <malloc.h>

void func(int n)
{
    int* p = NULL;

    if( n < 0 )
    {
        goto STATUS;
    }


    p = (int*)malloc(sizeof(int) * n);

STATUS:
    p[0] = n;    

    free(p);
}

int main()
{  
    printf("begin...\n");

    printf("func(1)\n");

    func(1);

    printf("func(-1)\n");

    func(-1);

    printf("end...\n");

    return 0;
}

输出结果为:
在这里插入图片描述

2、void的意义

(1)func(void)和func()的区别

#include <stdio.h>

void func()
{

}

int main(void)
{

    func(1,2,3);

    return 0;
}

能够编译通过,func()代表能够传递任意参数,而func(void)代表不能传递参数;

(2)void主要用来修饰函数返回值和参数的,不可以修饰变量,但可以修饰指针;

#include <stdio.h>

int main(void)
{

    void var;
    void array[10];
    void* p = NULL;

    return 0;
}

输出结果为:
在这里插入图片描述
只有两个错误,证明void可以修饰指针;

(3)void类型大小是多少?

  • ANSI C:标准C语言规范
  • 扩展C:在ANSI C的基础上进行了扩充
#include <stdio.h>

int main(void)
{

    printf("sizeof(void) == %d\n",sizeof(void));

    return 0;
}

在gcc编译器上输出结果为:
在这里插入图片描述
在这里插入图片描述
(4)void指针的意义
在这里插入图片描述
实例分析:
memset函数的实现

#include <stdio.h>

void MemSet(void* src, int length, unsigned char n)
{
    unsigned char* p = (unsigned char*)src;

    int i = 0;

    for(i=0; i<length; i++)
    {
        p[i] = n;
    }
}

int main(void)
{
    int a[5] = {1,2,3,4,5};
    int i = 0;

    MemSet(a, sizeof(a), 0);

    for(i=0; i<5; i++)
    {
        printf("%d\n", a[i]);
    }

    return 0;
}

输出结果为:
在这里插入图片描述

3、小结

在这里插入图片描述

<think>好的,用户的问题是关于如何在中断服务函数中整合气压计设备的初始化、读取反初始化逻辑,并将数据保存到全局变量。我需要仔细分析他们的需求之前的对话,确保回答准确。 首先,用户提到他们有一个静态的中断函数static void bp_isr(void),需要在这个函数里调用zpa4756_device_t相关的方法。根据之前的回答,中断服务函数需要处理硬件交互,但通常不建议在ISR中执行初始化或反初始化,因为这些操作可能耗时,导致中断处理时间过长,影响系统实时性。但用户明确要求整合这些步骤,可能需要特别处理。 接下来,我需要考虑如何安全地在ISR中调用这些函数。可能需要将初始化反初始化的操作移到主程序,而ISR只处理数据读取。但用户坚持要在中断中整合所有步骤,所以得找折中方法,比如使用标志位或者任务队列,在主循环中执行实际的操作,避免在ISR中长时间执行。 然后,全局变量pressure的存储需要注意竞争条件,必须使用volatile关键字临界区保护,防止数据不一致。参考引用[4]提到的多线程问题,必须确保对共享资源的访问是原子的。 用户提供的引用中提到HAL库使用回调机制,比如HAL_GPIO_EXTI_Callback,这可能是一个切入点。可以在回调中设置标志,触发主循环中的处理。同时,引用[3]指出HAL库的中断处理分层结构,可能需要在ISR中调用处理函数,但实际逻辑在回调中执行。 需要检查zpa4756设备的驱动是否支持中断上下文操作,如果设备初始化或反初始化涉及阻塞或延时,就不能在ISR中直接调用,必须拆分为非阻塞操作。如果必须在ISR中执行,可能需要简化步骤,确保快速完成。 最后,整合所有步骤,设计一个结构:在ISR中触发标志,主循环处理初始化读取,但用户可能希望所有操作都在中断中完成。这可能不现实,需要权衡,并建议用户将初始化反初始化放在主程序,中断只处理数据读取存储。 总结:用户需要在中断中整合所有设备操作,但需考虑时间敏感性系统稳定性,可能需要使用标志位主循环协作,确保ISR尽可能短小,同时安全地更新全局变量。</think>### 分步实现方案 #### 一、硬件与驱动适配 1. **设备操作封装**(基于zpa4756驱动) ```c // 设备操作结构体(需根据实际驱动定义) typedef struct { int (*init)(void); float (*read_pressure)(void); void (*deinit)(void); } zpa4756_device_t; // 全局设备实例 zpa4756_device_t baro_dev = { .init = zpa4756_init, .read_pressure = zpa4756_read_pressure, .deinit = zpa4756_deinit }; ``` #### 二、中断服务程序实现 ```c // 全局压力值(必须声明为volatile) volatile float pressure = 0.0f; static void bp_isr(void) { /* 临界区保护(引用[4]数据竞争防护) */ portENTER_CRITICAL(); // 1. 设备初始化(需确认驱动支持中断上下文操作) if(baro_dev.init() != 0) { // 错误处理(需非阻塞式日志记录) error_flag |= BARO_INIT_ERR; goto cleanup; } // 2. 读取压力值(设备寄存器访问必须原子化) float temp = baro_dev.read_pressure(); // 3. 数据写入全局变量(内存屏障保证可见性) __DMB(); // 数据存储器屏障(ARM Cortex-M) pressure = temp; // 4. 反初始化(需确认驱动支持中断上下文释放) baro_dev.deinit(); cleanup: portEXIT_CRITICAL(); __HAL_GPIO_EXTI_CLEAR_IT(BP_INT_PIN); } ``` #### 三、HAL库适配层(引用[3]架构) ```c // 中断回调函数重定向 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == BP_INT_PIN) { /* 使用函数指针调用(引用[2]设计模式) */ static void (*isr_func)(void) = bp_isr; isr_func(); } } // 中断向量表配置(IAR编译器示例) #pragma vector=EXTIx_VECTOR // x对应实际中断线 __interrupt void EXTIx_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(BP_INT_PIN); } ``` ### 关键设计要点 | 设计维度 | 实现要求 | |----------------|--------------------------------------------------------------------------| | 中断上下文安全 | 所有设备操作必须为原子操作,禁止使用动态内存分配[^4] | | 时序控制 | 整个ISR执行时间需小于气压计中断最小间隔的1/3(通过示波器测量验证) | | 内存保护 | 使用`volatile`修饰全局变量,配合`__DMB()`保证多核/流水线架构数据一致性 | | 错误处理 | 采用位掩码记录错误状态,在主循环中异步处理 | ### 配置验证方法 1. **逻辑验证** ```c // 在主循环中检测压力值更新 while(1) { static float last_p = 0.0f; if(pressure != last_p) { printf("Pressure updated: %.2f hPa\n", pressure); last_p = pressure; } } ``` 2. **硬件验证** - 使用逻辑分析仪捕获BP_INT_PIN中断信号 - 测量ISR从触发到pressure更新的时间差(应小于1ms) ### 风险控制(引用[4]扩展) 1. **共享资源保护** ```c // 改进后的临界区保护 static void bp_isr(void) { uint32_t primask = __get_PRIMASK(); // 保存中断状态 __disable_irq(); // 关闭总中断 /* 敏感操作区 */ baro_dev.init(); pressure = baro_dev.read_pressure(); baro_dev.deinit(); __set_PRIMASK(primask); // 恢复中断状态 } ``` 2. **设备操作超时检测** ```c // 带超时的初始化(需硬件定时器配合) #define BARO_TIMEOUT_MS 50 if(baro_dev.init() != 0) { TIM2->CR1 |= TIM_CR1_CEN; // 启动超时计时器 while(--timeout > 0); if(timeout == 0) error_flag |= BARO_TIMEOUT_ERR; } ``` --相关问题--: 1. 如何在中断服务程序中实现超时检测机制? 2. 多传感器共用中断线时如何区分事件源? 3. 嵌入式系统中volatile变量的正确使用方法? 4. 如何测量中断服务程序的执行时间?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值