C语言开发如何将错误扼杀在编译阶段

原创 嵌入式软件实战派 2023-10-17 08:43
你有没有想过,C语言一些简单的语法规则,可以做出很巧妙的方法。
举个例子,C语言的数组长度是不允许是负数的,当然常识中数组长度为负数好像也没什么意义。
例如int arr[1];是对的,而int arr[-1];是错的。
这个规则很简单,也很容易理解,当然也不会引人关注。
但是呢,我说可以用这个语法规则做C语言assert断言错误检查,你信么?
优秀的程序员,一般都是想尽一切办法将程序的错误尽可能地被拦截在运行之前,即编译阶段和预编译阶段的,而不是流出到运行阶段,更不是发生在用户手里的产品中。
对于预编译阶段的错误拦截,比较简单,通过#if#error等预编译指令就可以做到,例如
1. FreeRTOS中的Priority检查,用户必须将优先级定义大于或等于1,否则就报错
#if configMAX_PRIORITIES < 1      #error configMAX_PRIORITIES must be defined to be greater than or equal to 1.#endif
这是因为,程序设计里如果遇到configMAX_PRIORITIES < 1的情况,可能会导致更严重的错误。
所以这种在预编译阶段拦截了这个错误,是很有作用的
2. FreeRTOS中的运行时状态获取函数,必须依赖于configUSE_TRACE_FACILITY的定义
void vTaskGetRunTimeStats( char *pcWriteBuffer ){TaskStatus_t *pxTaskStatusArray;UBaseType_t uxArraySize, x;uint32_t ulTotalTime, ulStatsAsPercentage;#if( configUSE_TRACE_FACILITY != 1 ){    #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().}#endif

因为,这个函数很依赖其他其他的函数实现或者资源定义,那么这个configUSE_TRACE_FACILITY管理了这些

3. 版本检查,如果你设计一个比较通用的功能,可以用到很多项目中去,为了方便管理和迭代更新,就要对这个功能模块做版本定义。那么不同版本之间就存在差异,可能存在不兼容的情况,此时就可以用预编译指令做这种兼容的检查
#if (XXX_VER < 0x201)    #error "This version of XXX is too low!"#endif
通过预编译指令来检查错误是很有限的,因为预编译指令能检查的是立即数和一些逻辑关系。
于是,我们还要考虑编译阶段的错误检查,即对在编译阶段产生的结果做检查。
例如,unsigned long或者void*的长度检查即sizeof(unsigned long)sizeof(void*)的值,就必须在编译阶段
由于unsigned longvoid*的长度在不同芯片架构上可能存在差异,如果你的程序依赖这个类型的长度,那必须要检查其长度是否为你设计的那样。
像这种#if sizeof(unsigned long) == 4是不正确的做法,因为预编译无法计算sizeof(unsigned long)
那么有没有一种情况能确保sizeof(unsigned long)的值是4才不出错呢?
也许你会想到一个叫assert的东西,例如FreeRTOS里面定义的
#define configASSERT(x)   if((x)==0) { taskDISABLE_INTERRUPTS(); for( ;; ); }
既可以这样使用configASSERT(sizeof(unsigned long) == 4),如果sizeof(unsigned long) == 4,那么程序是“静悄悄”的,当做啥事都没发生,但是如果sizeof(unsigned long) == 8,那么程序就会进入for( ;; );无法自拔。
但是这种做法是运行阶段的,必须要在程序已经集成好并让其在实际环境中运行才能发现这种错误,排查起来成本还是有点高。
那么有没有一种办法可以让其扼杀在编译阶段呢?
答案是有的,像C++11和C11就支持这种assert了,名叫STATIC_ASSERT,需要包含头文件assert.h。
但是,如果我还没用到C11,也想用这种STATIC_ASSERT呢,有没有办法自己实现一个?
答案也是可以的,需要想个窍门,例如从数组的长度入手。
int arr[1];这种定义是没有问题的,但int arr[-1];却是会引起编译器报错的,我们就可以基于这种东西做文章了,即将数组长度换成检查条件COND,即如果COND为TRUE就不报错,为FALSE就报错
通过一顿反复尝试,搞成这样或许就可以了
int arr[(!!(COND))*2-1];
这里(!!(COND))*2-1没有一个符号是多余的,简单解释下:
1. (COND),这里加了一层括号,防止COND是个比较复杂的表达式,可能引发未知的优先级问题;
2. (!!(COND)),这里有两个感叹号,即逻辑取反再取反。也许你会觉得有点奇怪,!!TRUE不就是TURE吗,!!FALSE也是FALSE啊,双层取反是不是有点多余。那你就要认真思考下TRUE和FALSE的定义了,C语言中的FALSE是0,而非0是TRUE,这个非0就有很多发挥空间了,例如整数100,也是TRUE,但是!!100就会变成1,这里的(!!(COND))就是让其结果变成0或者1,而不是其他数

3. 通过2的解释,就很容易理解(!!(COND))*2-1这可以保证这个结果是1或者-1,而不存在其他数值。因为,我们的目的要的是int arr[1];或者int arr[-1];即可。

定义一个数组用在程序中间好像用起来没那么友好,可以换成
typedef int arr[(!!(COND))*2-1];
为了还能看到多一点点错误信息,还可以将其定义成宏,并带多一个参数,这就成了这样
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
在这个基础上再搞定其他的,就可以这样了
// token pasting madness:#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)#define COMPILE_TIME_ASSERT(X)    COMPILE_TIME_ASSERT2(X,__LINE__)
接着,判断sizeof(unsigned long) 是否为4,就可以这样
STATIC_ASSERT(sizeof(unsigned long) == 4,unsigned_long_size_is_not_4_error);
好了,内容就这么多了,这是不是很好玩?关注公众号“嵌入式软件实战派”,我给你分享更多知识和技巧。
本文参考:Static assert in C(https://stackoverflow.com/questions/3385515/static-assert-in-c)
如果这个文章能对你有帮助,请点个赞👍🏻点个在看,感谢!

如果你喜欢我的文章,请关注,并转发点赞在看,这是对我莫大的鼓励!

嵌入式软件实战派 专注嵌入式软件开发领域知识传授,包括C语言精粹,RTOS原理与使用,MCU驱动开发,AUTOSAR搭建,软件架构方法设计等。
评论 (0)
  • 在智能交互设备快速发展的今天,语音芯片作为人机交互的核心组件,其性能直接影响用户体验与产品竞争力。WT588F02B-8S语音芯片,凭借其静态功耗<5μA的卓越低功耗特性,成为物联网、智能家居、工业自动化等领域的理想选择,为设备赋予“听得懂、说得清”的智能化能力。一、核心优势:低功耗与高性能的完美结合超低待机功耗WT588F02B-8S在休眠模式下待机电流仅为5μA以下,显著延长了电池供电设备的续航能力。例如,在电子锁、气体检测仪等需长期待机的场景中,用户无需频繁更换电池,降低了维护成本。灵活的
    广州唯创电子 2025-04-02 08:34 50浏览
  • 职场之路并非一帆风顺,从初入职场的新人成长为团队中不可或缺的骨干,背后需要经历一系列内在的蜕变。许多人误以为只需努力工作便能顺利晋升,其实核心在于思维方式的更新。走出舒适区、打破旧有框架,正是让自己与众不同的重要法宝。在这条道路上,你不只需要扎实的技能,更需要敏锐的观察力、不断自省的精神和前瞻的格局。今天,就来聊聊那改变命运的三大思维转变,让你在职场上稳步前行。工作初期,总会遇到各式各样的难题。最初,我们习惯于围绕手头任务来制定计划,专注于眼前的目标。然而,职场的竞争从来不是单打独斗,而是团队协
    优思学院 2025-04-01 17:29 93浏览
  • 引言在语音芯片设计中,输出电路的设计直接影响音频质量与系统稳定性。WT588系列语音芯片(如WT588F02B、WT588F02A/04A/08A等),因其高集成度与灵活性被广泛应用于智能设备。然而,不同型号在硬件设计上存在关键差异,尤其是DAC加功放输出电路的配置要求。本文将从硬件架构、电路设计要点及选型建议三方面,解析WT588F02B与F02A/04A/08A的核心区别,帮助开发者高效完成产品设计。一、核心硬件差异对比WT588F02B与F02A/04A/08A系列芯片均支持PWM直推喇叭
    广州唯创电子 2025-04-01 08:53 145浏览
  • REACH和RoHS欧盟两项重要的环保法规有什么区别?适用范围有哪些?如何办理?REACH和RoHS是欧盟两项重要的环保法规,主要区别如下:一、核心定义与目标RoHS全称为《关于限制在电子电器设备中使用某些有害成分的指令》,旨在限制电子电器产品中的铅(Pb)、汞(Hg)、镉(Cd)、六价铬(Cr6+)、多溴联苯(PBBs)和多溴二苯醚(PBDEs)共6种物质,通过限制特定材料使用保障健康和环境安全REACH全称为《化学品的注册、评估、授权和限制》,覆盖欧盟市场所有化学品(食品和药品除外),通过登
    张工13144450251 2025-03-31 21:18 105浏览
  • 文/郭楚妤编辑/cc孙聪颖‍不久前,中国发展高层论坛 2025 年年会(CDF)刚刚落下帷幕。本次年会围绕 “全面释放发展动能,共促全球经济稳定增长” 这一主题,吸引了全球各界目光,众多重磅嘉宾的出席与发言成为舆论焦点。其中,韩国三星集团会长李在镕时隔两年的访华之行,更是引发广泛热议。一直以来,李在镕给外界的印象是不苟言笑。然而,在论坛开幕前一天,李在镕却意外打破固有形象。3 月 22 日,李在镕与高通公司总裁安蒙一同现身北京小米汽车工厂。小米方面极为重视此次会面,CEO 雷军亲自接待,小米副董
    华尔街科技眼 2025-04-01 19:39 81浏览
  •        在“软件定义汽车”的时代浪潮下,车载软件的重要性日益凸显,软件在整车成本中的比重逐步攀升,已成为汽车智能化、网联化、电动化发展的核心驱动力。车载软件的质量直接关系到车辆的安全性、可靠性以及用户体验,因此,构建一套科学、严谨、高效的车载软件研发流程,确保软件质量的稳定性和可控性,已成为行业共识和迫切需求。       作为汽车电子系统领域的杰出企业,经纬恒润深刻理解车载软件研发的复杂性和挑战性,致力于为O
    经纬恒润 2025-03-31 16:48 82浏览
  • 文/Leon编辑/cc孙聪颖‍步入 2025 年,国家进一步加大促消费、扩内需的政策力度,家电国补政策将持续贯穿全年。这一利好举措,为行业发展注入强劲的增长动力。(详情见:2025:消费提振要靠国补还是“看不见的手”?)但与此同时,也对家电企业在战略规划、产品打造以及市场营销等多个维度,提出了更为严苛的要求。在刚刚落幕的中国家电及消费电子博览会(AWE)上,家电行业的竞争呈现出胶着的态势,各大品牌为在激烈的市场竞争中脱颖而出,纷纷加大产品研发投入,积极推出新产品,试图提升产品附加值与市场竞争力。
    华尔街科技眼 2025-04-01 19:49 81浏览
  • 提到“质量”这两个字,我们不会忘记那些奠定基础的大师们:休哈特、戴明、朱兰、克劳士比、费根堡姆、石川馨、田口玄一……正是他们的思想和实践,构筑了现代质量管理的核心体系,也深远影响了无数企业和管理者。今天,就让我们一同致敬这些质量管理的先驱!(最近流行『吉卜力风格』AI插图,我们也来玩玩用『吉卜力风格』重绘质量大师画象)1. 休哈特:统计质量控制的奠基者沃尔特·A·休哈特,美国工程师、统计学家,被誉为“统计质量控制之父”。1924年,他提出世界上第一张控制图,并于1931年出版《产品制造质量的经济
    优思学院 2025-04-01 14:02 105浏览
  • 引言随着物联网和智能设备的快速发展,语音交互技术逐渐成为提升用户体验的核心功能之一。在此背景下,WT588E02B-8S语音芯片,凭借其创新的远程更新(OTA)功能、灵活定制能力及高集成度设计,成为智能设备语音方案的优选。本文将从技术特性、远程更新机制及典型应用场景三方面,解析该芯片的技术优势与实际应用价值。一、WT588E02B-8S语音芯片的核心技术特性高性能硬件架构WT588E02B-8S采用16位DSP内核,内部振荡频率达32MHz,支持16位PWM/DAC输出,可直接驱动8Ω/0.5W
    广州唯创电子 2025-04-01 08:38 133浏览
  • 据先科电子官方信息,其产品包装标签将于2024年5月1日进行全面升级。作为电子元器件行业资讯平台,大鱼芯城为您梳理本次变更的核心内容及影响:一、标签变更核心要点标签整合与环保优化变更前:卷盘、内盒及外箱需分别粘贴2张标签(含独立环保标识)。变更后:环保标识(RoHS/HAF/PbF)整合至单张标签,减少重复贴标流程。标签尺寸调整卷盘/内盒标签:尺寸由5030mm升级至**8040mm**,信息展示更清晰。外箱标签:尺寸统一为8040mm(原7040mm),提升一致性。关键信息新增新增LOT批次编
    大鱼芯城 2025-04-01 15:02 152浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦