嵌入式工程师C语言避坑小技巧

描述

为了做一个好的设计,本身在软硬件的配合上就需要克服无数的困难和障碍,任何一名 MCU 爱好者都不希望遇到一些因为语言和工具而产生的困扰,我们在 MCU 这种资源受限的平台上进行 C语言的开发虽然被软件界看起来不怎么高大上,但是 MCU 的开发目前 C 语言还是主流,为了更好的操控和调试我们的硬件,我们还是需要竭力的避免一些 C语言编程的陷阱,避免被一些高大上的变成语言或者架构干扰产品整体的进度和可靠性。

第1坑:不要使用“GOTO”语句

GOTO 语句最早源于汇编语言的跳转,在很多年前,计算机的变成还处于起步阶段,C语言开始也是寻着汇编的思路来设计的,因此就遗留下了这么一个 GOTO 语句,允许程序员自由的在代码间翱翔。使用GOTO语句的例子

#include
int main() { int i = 0;
// 使用goto语句的简单示例 goto start;
loop: printf("Inside loop: %dn", i); i++;
start: if (i < 5) goto loop;
printf("Loop finished.n");
return 0;}

这种 goto 语句用起来简单,但是整体程序如果来回跳转,读起来会非常的困难,非常绕,并且 GOTO 语句还存在以下问题:

  1. 可读性差: 使用goto语句的代码通常会变得难以理解,因为它允许在程序中跳转到不同的标签位置。这使得代码流程变得不清晰,增加了理解代码的难度。
  2. 难以维护: 当代码包含大量goto语句时,很容易导致代码的维护困难。修改代码或添加新功能时,必须仔细考虑goto语句的影响,以防止引入错误。
  3. 错误的使用可能导致问题: 如果不小心使用了错误的标签,或者在不当的位置使用goto,可能导致程序的不正确行为。这种错误可能难以追踪和修复。
  4. 不利于结构化编程: 使用goto语句可能违背结构化编程的原则,使得代码难以按照清晰的结构组织。结构化编程强调使用顺序结构、选择结构和循环结构来构建清晰、可读、可维护的代码。
  5. 不利于调试: 调试时,跳转语句会使程序的执行路径变得复杂,增加了调试的难度。代码中的跳转可能使得代码不易于单步调试,阻碍了查找和修复错误的过程。

第2坑:使用完整的条件语句

在使用判断语句的时候,我们尤其要注意判断条件的完整性,我们许多工程师都熟悉简单的if else 语句,然而有一些工程师却没有注意到,不同的写法可能会浪费一些处理器的时间。比如:

if(value == 1U)
{


}


if(value == 0U)
{


}


if(value == 1U)
{


}
else 
{


}

在第一种写法中,处理器会去判断两次,然后根据判断结果进行分支运行,但是如果我们写成第二种写法,处理器只需要判断一次就可以了。尤其是这种判断在一个大循环内部,这将浪费我们很多处理器时间。

另外为了代码具备更清晰的可读性,我们应该让 if else 成对出现,并且都是用{}把程序分割开来,这样也避免我们在调试的时候复制粘贴出现一些错误,从而影响我们调试和解决问题的进度。

#include
int main() { int choice;
// 提示用户输入数字 printf("Enter a number (1-3): "); scanf("%d", &choice);
// 使用 switch 语句根据用户输入执行不同的操作 switch (choice) { case 1: printf("You chose option 1.n"); // 执行操作1的代码 break;
case 2: printf("You chose option 2.n"); // 执行操作2的代码 break;
case 3: printf("You chose option 3.n"); // 执行操作3的代码 break;
default: printf("Invalid choice. Please enter a number between 1 and 3.n"); // 处理无效选择的代码 break; }
return 0;}

如果判断分支比较多,一定是用 swich case 语句来代替 if else。道理是相同的,一定要完整且用{}将程序段分隔好。同时要注意,如果我们对分支的命中率有一定的前瞻性,那么我们最好把命中率比较高的分支放在前面。

对于 case 比较多的情况,有些编译器会主动优化,这时候就不必考虑命中率的问题了。

第3坑:使用FOR(;;)还是 While(1)?

MCU 的开发过程中,我们绝大部分情况下还是在使用前后台系统,当然即便我们跑了一些实时性的操作系统,也避免不了使用一些无限循环的处理。

那么处理无限循环的语句目前有两种写法,我常看到一些初级工程师会使用 while(1),而在一些操作系统源码中看到的更多的是 for(;;)。

如果在 C99 的版本下,我们使用 for 来写循环看起来更紧凑。

// while 循环的初始化int i = 0;while (i < 5) {    // ...    i++;}
// for 循环的初始化for (int i = 0; i < 5; i++) { // ...}


另外,我十几年前在赛普拉斯的单片机上开发,因为 flash 空间很小,需要极致优化代码来进行空间压缩,这里我选择了 for 循环的写法可以让空间多出一个字节来,不过现在的很多编译器都已经更新了很多年了,至少在主流的 arm 平台上他们的汇编代码都是一样的了。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分