C语言学习专栏(1):易忘点

本文详细讲解了C语言中容易遗忘的关键知识点,包括#include头文件的区别、main函数的作用、预编译过程、静态库与动态库的差异、声明与定义的辨析、数值表示与溢出、赋值表达式、除法操作、类型转换以及数组、指针、函数、结构体等相关概念。此外,还介绍了多文件编程和指针操作,深入探讨了C语言的细节和陷阱,是C语言学习者的宝贵参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言学习专栏系列:

【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权);

本博客的内容来自于:C语言学习专栏(1):易忘点

学习、合作与交流联系q384660495;

本博客的内容仅供学习与参考,并非营利;


include< > 与 #include ""的区别:

  1. < > 表示系统直接按系统指定的目录检索;
  2. “” 表示系统先在 “” 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索;

main函数

  1. 一个完整的 C 语言程序,是由一个、且只能有一个 main()函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。
  2. main 函数是 C 语言程序的入口,程序是从 main 函数开始执行。

C语言编译过程

  1. 预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法 ;
  2. 编译:检查语法,将预处理后文件编译生成汇编文件;
  3. 汇编:将汇编文件生成目标文件(二进制文件);
  4. 链接:C 语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去;

在这里插入图片描述

静态库与动态库

  1. 静态库与动态库都是链接阶段的步骤。静态库(.a、.lib)和动态库(.so、.dll)。
  2. 静态库编译期间打包到可执行文件中,而动态库是执行时才被载入。
  3. 动态库规避了空间浪费和静态库在更新、部署和发布带来的麻烦。不同程序调用相同的动态库,内存中只需要保存一份实例。
  4. 静态库和动态库的最大区别,静态情况下,把库直接加载到程序中,而动态库链接的时候,它只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度,和降低程序的耦合度。
  5. 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。静态库在程序编译时被连接,运行时不再需要。

声明与定义

一般的情况下,把建立存储空间的声明称之为“定义”,而把不需要建立存储 空间的声明称之为“声明”。直接为声明赋值会报错。

原码、反码与补码

在计算机系统中,数值一律用补码来存储,主要原因是:

  1. 统一了零的编码
  2. 将符号位和其它位统一处理
  3. 将减法运算转变为加法运算
    在这里插入图片描述

赋值表达式的返回值

注意,赋值表达式有返回值,等于等号右边的值。变量y的值就是赋值表达式(x = 2 * x)的返回值2。赋值变量符是从右向左执行。

除法注意事项

  1. C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。
  2. 如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时 C 语言就会进行浮点数除法。

连续运算符

i < j < k

上面示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果,即不是保证变量j的值在i和k之间。因为表示运算符是从左到右计算,所以实际执行的是下面的表达式。

(i < j) < k

python是可以这样判断的

浮点数判断大小

注意,由于存在精度限制,浮点数只是一个近似值,它的计算是不精确的,比如 C 语言里面0.1 + 0.2并不等于0.3,而是有一个很小的误差。

bool类型

C99 标准添加了类型_Bool,表示布尔值。但是,这个类型其实只是整数类型的别名,还是使用0表示伪,1表示真。

头文件stdbool.h定义了另一个类型别名bool,并且定义了true代表1、false代表0。只要加载这个头文件,就可以使用这几个关键字。

#include <stdbool.h>

bool flag = false;

字面值的类型

一般情况下,十进制整数字面量(比如123)会被编译器指定为int类型。如果一个数值比较大,超出了int能够表示的范围,编译器会将其指定为long int。如果数值超过了long int,会被指定为unsigned long。如果还不够大,就指定为long long或unsigned long long。

unsigned int里面的int可以省略,所以上面的变量声明也可以写成下面这样。

unsigned a;

字符类型char也可以设置signed和unsigned。

signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0255

注意,C 语言规定char类型默认是否带有正负号,由当前系统决定。这就是说,char不等同于signed char,它有可能是signed char,也有可能是unsigned char。这一点与int不同,int就是等同于signed int。

溢出

每一种数据类型都有数值范围,如果存放的数值超出了这个范围(小于最小值或大于最大值),需要更多的二进制位存储,就会发生溢出。大于最大值,叫做向上溢出(overflow);小于最小值,叫做向下溢出(underflow)。

一般来说,编译器不会对溢出报错,会正常执行代码,但是会忽略多出来的二进制位,只保留剩下的位,这样往往会得到意想不到的结果。

unsigned int i = 5;
unsigned int j = 7;

if (i - j < 0) // 错误
  printf("negative\n");
else
  printf("positive\n");

上面示例的运算结果,会输出positive。原因是变量i和j都是 unsigned int 类型,i - j的结果也是这个类型,最小值为0,不可能得到小于0的结果。正确的写法是写成下面这样。

sizeof返回值

sizeof运算符的返回值,C 语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定,sizeof到底返回什么类型。不同的系统中,返回值的类型有可能是unsigned int,也有可能是unsigned long,甚至是unsigned long long,对应的printf()占位符分别是%u、%lu和%llu。这样不利于程序的可移植性。

C 语言提供了一个解决方法,创造了一个类型别名size_t,用来统一表示sizeof的返回值类型。该别名定义在stddef.h头文件(引入stdio.h时会自动引入)里面,对应当前系统的sizeof的返回值类型,可能是unsigned int,也可能是unsigned long。

C 语言还提供了一个常量SIZE_MAX,表示size_t可以表示的最大整数。所以,size_t能够表示的整数范围为[0, SIZE_MAX]。

printf()有专门的占位符%zd或%zu,用来处理size_t类型的值。

如果当前系统不支持%zd或%zu,可使用%u(unsigned int)或%lu(unsigned long int)代替。

类型转换

  1. 赋值运算符的自动转换

字节宽度较小的整数类型,赋值给字节宽度较大的整数变量时,会发生类型提升,即窄类型自动转为宽类型。

字节宽度较大的类型,赋值给字节宽度较小的变量时,会发生类型降级,自动转为后者的类型。这时可能会发生截值(truncation),系统会自动截去多余的二进制位,导致难以预料的结果。

  1. 运算符的自动转换

不同的浮点数类型混合运算时,宽度较小的类型转为宽度较大的类型,比如float转为double,double转为long double。

不同的整数类型混合运算时,宽度较小的类型会提升为宽度较大的类型。比如short转为int,int转为long等,有时还会将带符号的类型signed转为无符号unsigned。避免无符号与有符号的混合运算,会出现意想不到的错误。

  1. 函数的参数和返回值,会自动转成函数定义里指定的类型。

可移植类型

整数类型的极限值,有时候需要查看,当前系统不同整数类型的最大值和最小值,C 语言的头文件limits.h提供了相应的常量,比如SCHAR_MIN代表 signed char 类型的最小值-128,SCHAR_MAX代表 signed char 类型的最大值127。

为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使用这些常量。

SCHAR_MIN,SCHAR_MAX:signed char 的最小值和最大值。
SHRT_MIN,SHRT_MAX:short 的最小值和最大值。
INT_MIN,INT_MAX:int 的最小值和最大值。
LONG_MIN,LONG_MAX:long 的最小值和最大值。
LLONG_MIN,LLONG_MAX:long long 的最小值和最大值。
UCHAR_MAX:unsigned char 的最大值。
USHRT_MAX:unsigned short 的最大值。
UINT_MAX:unsigned int 的最大值。
ULONG_MAX:unsigned long 的最大值。
ULLONG_MAX:unsigned long long 的最大值。

程序员有时控制准确的字节宽度,这样的话,代码可以有更好的可移植性,头文件stdint.h创造了一些新的类型别名。

(1)精确宽度类型(exact-width integer type),保证某个整数类型的宽度是确定的。

int8_t8位有符号整数。
int16_t16位有符号整数。
int32_t32位有符号整数。
int64_t64位有符号整数。
uint8_t
<think>好的,我现在要帮用户修复这段代码的QAC警告。首先,我需要仔细阅读用户提供的代码,理解它的功能,然后分析可能存在的QAC警告类型。用户给出的函数是Mw_GetAndEnd_MeasureTime,看起来是用来测量时间差的,可能涉及定时器或调度器的相关操作。 首先,我注意到代码中有几个静态变量:end_uc_unit、end_trj和end_trj1。这些变量被声明为static,可能在多次函数调用之间保持状态。但QAC可能会警告这些静态变量没有被初始化,或者可能存在并发访问的问题,特别是在多任务环境中。不过,这个函数可能是在单线程环境下使用,所以需要确认是否确实需要静态存储。如果这些变量只是临时存储每次调用的结果,那么声明为静态可能不合适,会导致数据持久化,进而引发问题。 接下来,代码中有条件编译的部分,使用了SCHEDULER_DEBUG_CODE宏。这部分代码可能只在调试时启用,但需要注意变量end_cnt和end_system_tick_cnt是否已经声明,否则在非调试编译时可能会报错。不过用户的问题是关于QAC警告,可能涉及未使用的变量或者宏定义相关的问题。 然后,函数内部调用了App_Scheduler_Get_TimerValue()两次,分别赋值给end_trj和end_trj1,之后比较这两个值。如果end_trj1小于end_trj,说明可能在两次调用之间发生了定时器的溢出,所以重新获取end_uc_unit的值。这部分逻辑可能用于处理定时器溢出的情况,但需要确认是否正确处理了所有可能的溢出情况。 在计算diff_units时,如果end_uc_unit大于等于start_uc_unit,直接相减;否则,将end_uc_unit加上0xFFFFFFFF再减去start_uc_unit。这里使用了uint64_t类型来避免溢出,但QAC可能对类型转换和显式转换有警告,比如强制转换是否正确,或者是否有更清晰的写法。 接下来,我需要考虑常见的QAC警告类型,比如: 1. 静态变量未初始化:静态变量在C语言中默认初始化为0,但显式初始化更安全,避免依赖编译器行为。 2. 变量未声明:比如end_cnt和end_system_tick_cnt是否在宏启用时已声明。 3. 类型转换问题:比如将uint64_t转换为uint32_t时可能的数据丢失。 4. 可能的整数溢出:在计算diff_units时,虽然使用了uint64_t,但转换回uint32_t是否安全? 5. 代码可读性问题:复杂的表达式可能需要拆分,或者添加注释。 6. 函数副作用:静态变量可能导致函数不可重入,如果在多线程或中断中使用会有问题。 7. 条件判断中的冗余代码:比如end_trj1的比较部分是否有必要,或者是否可以优化。 首先处理静态变量未初始化的问题。将static变量显式初始化为0u,比如: static uint32_t end_uc_unit = 0u; static uint32_t end_trj = 0u; static uint32_t end_trj1 = 0u; 这样避免QAC警告可能的“Static variable not explicitly initialized”。 然后,检查条件编译部分。如果SCHEDULER_DEBUG_CODE为1U,那么end_cnt和end_system_tick_cnt必须被声明,否则编译时会报错。需要确保这些变量在宏启用时有定义,或者在函数外部声明。如果用户没有提供这部分代码,可能需要添加条件编译的声明,或者确保在非调试情况下这些变量不会引发错误。不过这可能不是QAC警告的重,但需要注意。 接下来,处理end_trj和end_trj1的比较部分。这里可能存在的警告是变量在赋值后未被使用,或者可能的竞争条件。比如,end_trj被赋值后,在if条件中比较end_trj1和end_trj,但end_trj1是在之后赋值的。这可能是因为担心在两次读取定时器之间发生溢出,所以需要重新获取end_uc_unit。这部分逻辑是否正确需要看定时器的实现,但代码本身可能没有问题,但QAC可能警告变量未使用,或者代码冗余。 例如,如果end_trj在赋值后,紧接着end_trj1被赋值,但之后end_trj只用于比较,之后不再使用。可能可以简化这部分代码,或者合并步骤。 然后,计算diff_units的部分,使用了强制转换到uint64_t,但最后返回的是uint32_t。QAC可能警告显式转换可能丢失数据,但在这个上下文中,由于计算的是时间差,且定时器的最大值是32位,所以结果应该在uint32_t范围内,所以转换是安全的。不过,可以添加注释说明这一,或者使用显式的掩码操作,比如与0xFFFFFFFF进行按位与,来确保结果正确。 另外,代码中的表达式: diff_units = (uint32_t)( ((uint64_t)end_uc_unit + (uint64_t)0xffffffffu )- start_uc_unit ); 这里可能的问题在于,当end_uc_unit小于start_uc_unit时,实际上是计算(end_uc_unit - start_uc_unit)的补码,即相当于end_uc_unit + (0xFFFFFFFF - start_uc_unit + 1),也就是(end_uc_unit + (0xFFFFFFFF - start_uc_unit) +1),不过这可能更清晰的写法是(end_uc_unit - start_uc_unit)的模数运算,因为对于无符号数,减法会自动处理溢出。不过用户当前的写法是否正确? 例如,假设start_uc_unit是100,end_uc_unit是50(因为定时器溢出),那么正确的差值应该是50 + 0xFFFFFFFF -100 +1 = 0xFFFFFFFF -50 = 0xFFFFFFCE(十进制4294967246)。但用无符号数的减法,end_uc_unit - start_uc_unit在32位情况下会自动得到正确的结果,即50 -100 = 4294967196(因为无符号溢出)。因此,用户原来的代码可能可以简化为: diff_units = end_uc_unit - start_uc_unit; 因为对于无符号数,即使end_uc_unit < start_uc_unit,减法结果也会自动处理为模数运算,得到正确的时间差。这样就不需要复杂的条件判断和64位运算。这可能是一个优化,同时也能消除QAC关于类型转换和复杂表达式的警告。 所以,原来的代码中的if-else分支可能没有必要,可以简化。这可能也是QAC警告的一个,比如“Redundant code”或者“Overcomplicated expression”。 总结可能的修复步骤: 1. 显式初始化静态变量。 2. 简化时间差计算,直接使用无符号数的减法,去掉条件判断和64位运算。 3. 处理条件编译中的变量声明问题,确保宏启用时变量存在。 4. 检查end_trj和end_trj1的使用,是否有冗余或未使用的变量。 5. 添加注释提高代码可读性。 6. 确保所有类型转换安全,避免QAC警告。 现在,针对时间差计算的部分,如果改为直接使用减法,可以简化代码并消除相关警告。例如: uint32_t diff_units = end_uc_unit - start_uc_unit; 因为对于uint32_t类型,end_uc_unit - start_uc_unit会自动处理溢出,得到正确的时间差,不需要条件判断。这样不仅简化了代码,还避免了可能的QAC警告,比如“Useless type conversion”或者“Overcomplicated expression”。 然后,关于end_trj和end_trj1的比较部分,可能的问题在于,如果在两次调用App_Scheduler_Get_TimerValue()之间定时器发生了溢出,那么end_trj1可能会小于end_trj。此时,用户重新获取end_uc_unit的值,可能是为了确保在定时器溢出时获取最新的计数器值。这部分逻辑是否正确?假设App_Scheduler_Get_TimerValue()返回的是定时器的当前值,而SysTimer_GetCurrentCounter()是另一个定时器的值,或者同一定时器的不同部分。需要确认这里的逻辑是否正确,是否可能导致end_uc_unit在溢出时被正确更新。 例如,当end_trj1 < end_trj时,说明在两次获取定时器值之间,定时器可能发生了溢出(比如,定时器是递增的,达到最大值后归零)。此时,重新获取end_uc_unit(可能是另一个高精度定时器的值)以确保准确性。这部分逻辑可能需要保留,但可以优化代码结构,减少变量使用。 可能的QAC警告是end_trj被赋值后未被使用,除了在比较中。例如,end_trj被赋值为第一次App_Scheduler_Get_TimerValue(),然后end_trj1被赋值为第二次的值,之后比较两者,但之后end_trj不再使用。因此,可以合并代码,比如: end_trj = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); end_trj1 = App_Scheduler_Get_TimerValue(); 可以改为: do { end_trj = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); end_trj1 = App_Scheduler_Get_TimerValue(); } while (end_trj1 < end_trj); 但这样可能会引入循环,如果定时器一直在快速溢出,可能导致死循环。因此,用户原来的代码可能只是尝试在检测到溢出时重新获取end_uc_unit,但只执行一次。这可能不足以处理所有情况,但这是用户原有的逻辑。 不过,这样的代码可能导致QAC警告,比如变量end_trj被赋值后未被充分使用,或者代码冗余。可以考虑移除end_trj,直接比较end_trj1和之前的定时器值,但需要保持原有逻辑。 或者,可能可以简化为: end_uc_unit = SysTimer_GetCurrentCounter(); end_trj1 = App_Scheduler_Get_TimerValue(); if (end_trj1 < previous_trj_value) { end_uc_unit = SysTimer_GetCurrentCounter(); } 但这里可能需要更多的上下文。 总之,这部分代码可能引发QAC关于变量未使用或冗余赋值的警告。例如,end_trj被赋值后,仅在if条件中使用,之后不再使用。因此,可以尝试合并步骤,减少变量使用。比如,直接比较两次获取的定时器值: uint32_t trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); uint32_t trj_after = App_Scheduler_Get_TimerValue(); if (trj_after < trj_before) { end_uc_unit = SysTimer_GetCurrentCounter(); } 这样,变量名更清晰,且避免了使用end_trj和end_trj1,减少变量数量,可能消除QAC关于变量未使用的警告。 接下来,处理静态变量的问题。如果这些变量确实需要在多次调用间保持状态,那么可能需要保留static,但需要考虑线程安全。如果函数被多个线程调用或在中断中使用,静态变量会导致竞争条件。但用户的问题可能不涉及多线程,所以显式初始化即可。 综合以上分析,修复步骤可能包括: 1. 显式初始化静态变量。 2. 简化时间差计算,使用无符号减法。 3. 优化定时器读取逻辑,减少变量使用。 4. 处理条件编译部分的变量声明。 5. 添加必要的注释。 现在,将这些步骤转化为代码修改: 原代码: static uint32_t end_uc_unit = 0u; static uint32_t end_trj = 0u; static uint32_t end_trj1 = 0u; 修改为显式初始化: static uint32_t end_uc_unit = 0u; static uint32_t end_trj = 0u; static uint32_t end_trj1 = 0u; (已经初始化,可能没有问题) 时间差计算部分: 原代码: if(end_uc_unit >= start_uc_unit) { diff_units = end_uc_unit - start_uc_unit; } else { diff_units = (uint32_t)( ((uint64_t)end_uc_unit + (uint64_t)0xffffffffu )- start_uc_unit ); } 可以简化为: diff_units = end_uc_unit - start_uc_unit; 因为对于无符号数,减法会自动处理溢出,得到正确的时间差。因此,整个if-else结构可以删除,直接赋值。 这将消除QAC关于复杂类型转换和条件冗余的警告。 关于定时器读取部分: 原代码: end_trj = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); end_trj1 = App_Scheduler_Get_TimerValue(); if(end_trj1 < end_trj) { end_uc_unit = SysTimer_GetCurrentCounter(); } 可以优化为: uint32_t trj_before, trj_after; do { trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); trj_after = App_Scheduler_Get_TimerValue(); } while (trj_after < trj_before); 这样可以确保在定时器没有溢出的情况下获取一致的trj_before和trj_after,避免在读取过程中定时器溢出导致的时间差错误。但使用循环可能会增加执行时间,特别是在高频定时器下。不过,这可能更可靠,而原代码只检查一次,可能不够。不过,用户原有逻辑可能只是尝试一次重新读取,因此可能需要保持原逻辑,但可以更改变量名以减少冗余。 或者,将end_trj和end_trj1改为局部变量,而非静态,因为它们在每次调用中都是临时的: uint32_t trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); uint32_t trj_after = App_Scheduler_Get_TimerValue(); if (trj_after < trj_before) { end_uc_unit = SysTimer_GetCurrentCounter(); } 这样可以消除静态变量end_trj和end_trj1,从而避免QAC关于静态变量可能导致的并发问题或持久化问题。因为原来的静态变量可能没有必要,因为每次调用函数时,这些值都是新的,不需要保持状态。所以,将end_trj和end_trj1改为局部变量,可以解决静态变量相关的QAC警告。 这样修改后,静态变量只剩下end_uc_unit,但原代码中end_uc_unit是static,可能用于保存最后一次的值,但函数名为GetAndEnd_MeasureTime,可能意味着每次调用都会更新结束时间,所以end_uc_unit作为静态变量可能保存了上一次调用的值,这可能导致错误。例如,如果函数被多次调用,静态变量会保留之前的值,导致计算的时间差不正确。 哦,这里发现一个严重问题:start_uc_unit可能是在另一个函数中保存的开始时间,而end_uc_unit是当前结束时间。如果end_uc_unit是静态变量,那么每次调用Mw_GetAndEnd_MeasureTime都会更新它,但start_uc_unit可能是在Mw_Start_MeasureTime中保存的静态变量?需要确认start_uc_unit的来源。如果start_uc_unit是全局变量或静态变量,而Mw_GetAndEnd_MeasureTime函数用来计算当前时间与start_uc_unit的差,那么end_uc_unit作为静态变量可能没有必要,应该每次获取当前值。 例如,正确的逻辑应该是: start_uc_unit在开始测量时记录,然后在结束时获取当前的end_uc_unit,计算差值。因此,end_uc_unit不应是静态变量,而应该是每次函数调用时的当前值。将其声明为静态会导致每次调用函数时,end_uc_unit保留上一次的值,这显然错误,会导致计算的时间差不正确。 因此,原代码中的end_uc_unit、end_trj、end_trj1被错误地声明为静态变量,这会导致函数不可重入,且在多次调用时返回错误的结果。这是严重的逻辑错误,也会导致QAC警告,比如“Persistent static variable may lead to unpredictable behavior”或类似的。 因此,必须将这些静态变量改为局部变量。例如: uint32_t Mw_GetAndEnd_MeasureTime(void) { uint32_t end_uc_unit; uint32_t trj_before, trj_after; #if (SCHEDULER_DEBUG_CODE == 1U) end_cnt++; end_system_tick_cnt = system_tick_cnt; #endif trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); trj_after = App_Scheduler_Get_TimerValue(); if (trj_after < trj_before) { end_uc_unit = SysTimer_GetCurrentCounter(); } uint32_t diff_units = end_uc_unit - start_uc_unit; return diff_units; } 这样修改后,去除了不必要的静态变量,解决了静态变量导致的QAC警告,同时修复了函数逻辑错误。 总结问题: 1. 静态变量错误使用,导致函数不可重入且结果错误。 2. 时间差计算过于复杂,可以简化为无符号减法。 3. 可能的变量未初始化(但原代码中静态变量默认初始化为0)。 4. 条件编译部分的变量声明问题。 5. 冗余的变量和代码结构。 修复步骤: 1. 将静态变量改为局部变量,因为每次调用函数都应获取当前时间。 2. 简化时间差计算,使用无符号减法。 3. 确保条件编译中的变量已声明。 4. 优化定时器读取逻辑,使用局部变量避免静态存储。 5. 添加注释说明关键步骤。 修改后的代码可能如下: uint32_t Mw_GetAndEnd_MeasureTime(void) { uint32_t end_uc_unit; uint32_t trj_before, trj_after; #if (SCHEDULER_DEBUG_CODE == 1U) /* 调试代码:增加计数并记录系统节拍 */ static uint32_t end_cnt = 0u; // 如果end_cnt未声明,需在此处或外部声明 extern uint32_t end_system_tick_cnt; // 假设在外部定义 end_cnt++; end_system_tick_cnt = system_tick_cnt; // 假设system_tick_cnt已定义 #endif /* 获取定时器值,处理可能的溢出 */ trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); trj_after = App_Scheduler_Get_TimerValue(); /* 如果发生溢出,重新获取当前计数器值 */ if (trj_after < trj_before) { end_uc_unit = SysTimer_GetCurrentCounter(); } /* 计算时间差(无符号减法自动处理溢出) */ uint32_t diff_units = end_uc_unit - start_uc_unit; return diff_units; } 这样修改后: - 静态变量被移除,解决了不可重入问题。 - 时间差计算简化,消除QAC警告。 - 添加了注释,提高可读性。 - 处理了条件编译中的变量声明问题(假设end_cnt和end_system_tick_cnt在适当的位置声明)。 需要注意的是,start_uc_unit应该是另一个函数(如Mw_Start_MeasureTime)中记录的静态变量或全局变量,否则函数无法正确计算时间差。因此,需要确保start_uc_unit的存储方式正确,但这可能超出当前函数的修改范围。 最后,检查所有可能的QAC警告是否已解决: - 静态变量移除,解决相关警告。 - 简化计算,消除类型转换和复杂表达式警告。 - 显式声明和初始化变量,避免未初始化警告。 - 代码结构更清晰,减少冗余。 因此,最终的修复代码应该类似上述修改后的版本,同时确保所有使用的变量正确声明,尤其是在调试宏启用时。</think>以下是针对该代码的QAC警告修复方案及代码优化建议,主要解决静态变量滥用、时间差计算优化和代码可读性问题: ```c uint32_t Mw_GetAndEnd_MeasureTime(void) { uint32_t end_uc_unit; uint32_t trj_before, trj_after; #if (SCHEDULER_DEBUG_CODE == 1U) /* 调试代码:增加计数并记录系统节拍 */ static uint32_t end_cnt = 0u; // 需在文件作用域声明 extern uint32_t end_system_tick_cnt;// 需在外部定义 end_cnt++; end_system_tick_cnt = system_tick_cnt; // 假设system_tick_cnt已定义 #endif /* 获取定时器值序列(处理定时器翻转)*/ trj_before = App_Scheduler_Get_TimerValue(); end_uc_unit = SysTimer_GetCurrentCounter(); trj_after = App_Scheduler_Get_TimerValue(); /* 检测定时器翻转并重新采样 */ if (trj_after < trj_before) { end_uc_unit = SysTimer_GetCurrentCounter(); } /* 无符号减法自动处理计数器翻转 */ return end_uc_unit - start_uc_unit; } ``` 主要修改说明: 1. **静态变量问题** - 原代码问题:错误使用`static`存储类 - `end_uc_unit`/`end_trj`/`end_trj1`改为局部变量 - 避免函数不可重入问题(多次调用相互影响) - 解决QAC警告:`[MISRA-C:2012 Rule 8.10]`(函数不可重入风险) 2. **时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的爱人是绿色

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值