0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式开发中的C语言编程思想

STM32嵌入式开发 来源:STM32嵌入式开发 作者:STM32嵌入式开发 2022-04-08 11:03 次阅读

摘要

本文首先分析了C语言的陷阱和缺陷,对容易犯错的地方进行归纳整理;分析了编译器语义检查的不足之处并给出防范措施,以Keil MDK编译器为例,介绍了该编译器的特性、对未定义行为的处理以及一些高级应用;在此基础上,介绍了防御性编程的概念,提出了编程过程中就应该防范于未然的多种措施;提出了测试对编写优质嵌入式程序的重要作用以及常用测试方法;最后,本文试图以更高的层次看待编程,讨论一些通用的编程思想。

1 编程风格

《计算机程序的构造和解释》一书在开篇写到:程序写出来是给人看的,附带能在机器上运行。

1.1 整洁的样式

使用什么样的编码样式一直都颇具争议性的,比如缩进和大括号的位置。因为编码的样式也会影响程序的可读性,面对一个乱放括号、对齐都不一致的源码,我们很难提起阅读它的兴趣。我们总要看别人的程序,如果彼此编码样式相近,读起源码来会觉得比较舒适。但是编码风格的问题是主观的,永远不可能在编码风格上达成统一意见。因此只要你的编码样式整洁、结构清晰就足够了。除此之外,对编码样式再没有其它要求。

提出匈牙利命名法的程序员、前微软首席架构师Charles Simonyi说:我觉得代码清单带给人的愉快同整洁的家差不多。你一眼就能分辨出家里是杂乱无章还是整洁如新。这也许意义不大。因为光是房子整洁说明不了什么,它仍可能藏污纳垢!但是第一印象很重要,它至少反映了程序的某些方面。我敢打赌,我在3米开外就能看出程序拙劣与否。我也许没法保证它很不错,但如果从3米外看起来就很糟,我敢保证这程序写得不用心。如果写得不用心,那它在逻辑上也许就不会优美。

1.2 清晰的命名

变量、函数、宏等等都需要命名,清晰的命名是优秀代码的特点之一。命名的要点之一是名称应能清晰的描述这个对象,以至于一个初级程序员也能不费力的读懂你的代码逻辑。我们写的代码主要给谁看是需要思考的:给自己、给编译器还是给别人看?我觉得代码最主要的是给别人看,其次是给自己看。如果没有一个清晰的命名,别人在维护你的程序时很难在整个全貌上看清代码,因为要记住十多个以上的糟糕命名的变量是件非常困难的事;而且一段时间之后你回过头来看自己的代码,很有可能不记得那些糟糕命名的变量是什么意思。

为对象起一个清晰的名字并不是简单的事情。首先能认识到名称的重要性需要有一个过程,这也许跟谭式C程序教材被大学广泛使用有关:满书的a、b、c、x、y、z变量名是很难在关键的初学阶段给人传达优秀编程思想的;其次如何恰当的为对象命名也很有挑战性,要准确、无歧义、不罗嗦,要对英文有一定水平,所有这些都要满足时,就会变得很困难;此外,命名还需要考虑整体一致性,在同一个项目中要有统一的风格,坚持这种风格也并不容易。

关于如何命名,Charles Simonyi说:面对一个具备某些属性的结构,不要随随便便地取个名字,然后让所有人去琢磨名字和属性之间有什么关联,你应该把属性本身,用作结构的名字。

1.3 恰当的注释

注释向来也是争议之一,不加注释和过多的注释我都是反对的。不加注释的代码显然是很糟糕的,但过多的注释也会妨碍程序的可读性,由于注释可能存在的歧义,有可能会误解程序真实意图,此外,过多的注释会增加程序员不必要的时间。如果你的编码样式整洁、命名又很清晰,那么,你的代码可读性不会差到哪去,而注释的本意就是为了便于理解程序。

这里建议使用良好的编码样式和清晰的命名来减少注释,对模块、函数、变量、数据结构、算法和关键代码做注释,应重视注释的质量而不是数量。如果你需要一大段注释才能说清楚程序做什么,那么你应该注意了:是否是因为程序变量命名不够清晰,或者代码逻辑过于混乱,这个时候你应该考虑的可能就不是注释,而是如何精简这个程序了。

2 数据结构

数据结构是程序设计的基础。在设计程序之前,应该先考虑好所需要的数据结构。

前微软首席架构师Charles Simonyi:编程的第一步是想象。就是要在脑海中对来龙去脉有极为清晰的把握。在这个初始阶段,我会使用纸和铅笔。我只是信手涂鸦,并不写代码。我也许会画些方框或箭头,但基本上只是涂鸦,因为真正的想法在我脑海里。我喜欢想象那些有待维护的结构,那些结构代表着我想编码的真实世界。一旦这个结构考虑得相当严谨和明确,我便开始写代码。我会坐到终端前,或者换在以前的话,就会拿张白纸,开始写代码。这相当容易。我只要把头脑中的想法变换成代码写下来,我知道结果应该是什么样的。大部分代码会水到渠成,不过我维护的那些数据结构才是关键。我会先想好数据结构,并在整个编码过程中将它们牢记于心。

开发过以太网操作系统SDS 940的Butler Lampson:(程序员)最重要的素质是能够把问题的解决方案组织成容易操控的结构。

开发CP/M操作系统的Gary.A:如果不能确认数据结构是正确的,我是决不会开始编码的。我会先画数据结构,然后花很长时间思考数据结构。在确定数据结构之后我就开始写一些小段的代码,并不断地改善和监测。在编码过程中进行测试可以确保所做的修改是局部的,并且如果有什么问题的话,能够马上发现。

微软创始人比尔**·**盖茨:编写程序最重要的部分是设计数据结构。接下来重要的部分是分解各种代码块。

编写世界上第一个电子表格软件的Dan Bricklin:在我看来,写程序最重要的部分是设计数据结构,此外,你还必须知道人机界面会是什么样的。

我们举个例子来说明。在介绍防御性编程的时候,提到公司使用的LCD显示屏抗干扰能力一般,为了提高LCD的稳定性,需要定期读出LCD内部的关键寄存器值,然后跟存在Flash中的初始值相比较。需要读出的LCD寄存器有十多个,从每个寄存器读出的值也不尽相同,从1个到8个字节都有可能。如果不考虑数据结构,编写出的程序将会很冗长。

void lcd_redu(void)

{ 。

读第一个寄存器值;

if(第一个寄存器值==Flash存储值)

{

读第二个寄存器值;

if(第二个寄存器值==Flash存储值)

{

。..

读第十个寄存器值;

if(第十个寄存器值==Flash存储值)

{

返回;

}

else

{

重新初始化LCD;

}

}

else

{

重新初始化LCD;

}

}

else

{

重新初始化LCD;

}

}

我们分析这个过程,发现能提取出很多相同的元素,比如每次读LCD寄存器都需要该寄存器的命令号,都会经过读寄存器、判断值是否相同、处理异常情况这一过程。所以我们可以提取一些相同的元素,组织成数据结构,用统一的方法去处理这些数据,将数据与处理过程分开来。

我们可以先提取相同的元素,将之组织成数据结构:

c2ecb04c-b659-11ec-aa7f-dac502259ad0.png

这里lcd_command表示的是LCD寄存器命令号;lcd_get_value是一个数组,表示寄存器要初始化的值,这是因为对于一个LCD寄存器,可能要初始化多个字节,这是硬件特性决定的;lcd_value_num是指一个寄存器要多少个字节的初值,这是因为每一个寄存器的初值数目是不同的,我们用同一个方法处理数据时,是需要这个信息的。

就本例而言,我们将要处理的数据都是事先固定的,所以定义好数据结构后,我们可以将这些数据组织成表格:

/*LCD部分寄存器设置值列表*/

lcd_redu_list_struct const lcd_redu_list_str[]= {

{SSD1963_Get_Address_Mode,{0x20}

,1}, /*1*/

{SSD1963_Get_Pll_Mn

,{0x3b,0x02,0x04}

,3}, /*2*/

{SSD1963_Get_Pll_Status

,{0x04}

,1}, /*3*

{SSD1963_Get_Lcd_Mode

,{0x24,0x20,0x01,0xdf,0x01,0x0f,0x00}

,7}, /*4*/

{SSD1963_Get_Hori_Period ,{0x02,0x0c,0x00,0x2a,0x07,0x00,0x00,0x00},8}, /*5*/

{SSD1963_Get_Vert_Period ,{0x01,0x1d,0x00,0x0b,0x09,0x00,0x00}

,7}, /*6*/ {SSD1963_Get_Power_Mode ,{0x1c}

,1}, /*7*/ {SSD1963_Get_Display_Mode,{0x03}

,1}, /*8*/ {SSD1963_Get_Gpio_Conf ,{0x0F,0x01}

,2}, /*9*/ {SSD1963_Get_Lshift_Freq ,{0x00,0xb8}

,2}, /*10* };

至此,我们就可以用一个处理过程来完成数十个LCD寄存器的读取、判断和异常处理了:

/** * lcd 显示冗余

* 每隔一段时间调用该程序一次 */ void lcd_redu(void) {

uint8_t tmp[8];

uint32_t i,j;

uint32_t lcd_init_flag;

lcd_init_flag =0;

for(i=0;i《sizeof(lcd_redu_list_str)/sizeof(lcd_redu_list_str[0]);i++)

{

LCD_SendCommand(lcd_redu_list_str[i].lcd_command);

uyDelay(10);

for(j=0;j《lcd_redu_list_str[i].lcd_value_num;j++)

{

tmp[j]=LCD_ReadData();

if(tmp[j]!=lcd_redu_list_str[i].lcd_get_value[j])

{

lcd_init_flag=0x55;

//一些调试语句,打印出错的具体信息

goto handle_lcd_init;

}

}

}

handle_lcd_init:

if(lcd_init_flag==0x55)

{

//重新初始化LCD

//一些必要的恢复措施

}

}

通过合理的数据结构,我们可以将数据和处理过程分开,LCD冗余判断过程可以用很简洁的代码来实现。更重要的是,将数据和处理过程分开更有利于代码的维护。比如,通过实验发现,我们还需要增加一个LCD寄存器的值进行判断,这时候只需要将新增加的寄存器信息按照数据结构格式,放到LCD寄存器设置值列表中的任意位置即可,不用增加任何处理代码即可实现!这仅仅是数据结构的优势之一,使用数据结构还能简化编程,使复杂过程变的简单,这个只有实际编程后才会有更深的理解。

审核编辑 :李倩

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

    关注

    180

    文章

    7596

    浏览量

    136008
  • 嵌入式开发
    +关注

    关注

    18

    文章

    1019

    浏览量

    47490

原文标题:嵌入式开发中的C语言编程思想

文章出处:【微信号:c-stm32,微信公众号:STM32嵌入式开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    零基础嵌入式开发学习路线

    嵌入式开发”没有接触过的同学可能会不明觉厉,但是只要你了解了,感兴趣并且有一个正确的学习路线的话,零基础也能入门。给大家介绍一个简单易懂的学习路线,让你能够从零开始学习嵌入式开发。 第一步:掌握
    发表于 10-25 15:55

    聚焦嵌入式开发的合规性工具、项目管理工具、版本迭代工具应用

    ,就嵌入式开发与管理领域的最新趋势、工具选择以及DevSecOps实践应用等方面展开了深入探讨。 本期对话龙智资深DevSecOps顾问徐晨晖, 分享嵌入式开发的合规性工具、项目管理和版本管理的工具选择,以及DevSecOps
    的头像 发表于 07-29 15:15 452次阅读

    嵌入式开发前景怎么样?

    嵌入式开发前景非常广阔,这主要得益于物联网、人工智能、大数据等技术的快速发展,以及嵌入式系统在各个领域的广泛应用。以下是对嵌入式开发前景的详细分析
    的头像 发表于 07-10 09:00 2425次阅读
    <b class='flag-5'>嵌入式开发</b>前景怎么样?

    C语言嵌入式开发的关键编译器角色

    嵌入式程序开发跟硬件密切相关,需要使用C语言来读写底层寄存器、存取数据、控制硬件等,C语言和硬件
    发表于 04-26 14:53 524次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>:<b class='flag-5'>嵌入式开发</b><b class='flag-5'>中</b>的关键编译器角色

    如何成为一名嵌入式C语言高手?

    如何成为一名嵌入式C语言高手? 嵌入式系统是当今科技领域的核心,而C语言则是
    发表于 04-07 16:03

    如何成为一名嵌入式C语言高手?

    如何成为一名嵌入式C语言高手? 嵌入式系统是当今科技领域的核心,而C语言则是
    发表于 03-25 14:12

    fpga是嵌入式开发

    FPGA(现场可编程门阵列)与嵌入式开发之间确实存在一定的关联,但它们在本质上是两个不同的领域。
    的头像 发表于 03-15 14:18 959次阅读

    聊一聊嵌入式C语言

    作为一名嵌入式软件开发者,熟练掌握嵌入式C语言对我的日常工作至关重要。
    的头像 发表于 01-22 09:28 517次阅读

    嵌入式开发常见的C语言技巧与方法分享

    嵌入式开发,常常要操作寄存器,对寄存器进行写入,读出等等操作。每个寄存器都有自己固有的地址,通过C语言访问这些地址就变得尤为重要。
    的头像 发表于 12-26 09:55 1039次阅读

    嵌入式开发C语言中的uint8_t科普

    嵌入式开发C语言代码,经常可以看到类似uint8_t、uint16_t、uint32_t、uint64_t这种数据类型,在教材
    的头像 发表于 12-13 16:30 6505次阅读
    <b class='flag-5'>嵌入式开发</b><b class='flag-5'>C</b><b class='flag-5'>语言</b>中的uint8_t科普

    嵌入式开发测试秘诀

    嵌入式软件开发过程中,花在测试和花在编码的时间比通常在3:1左右(实际上可能更多)。这个比例会随着工程师编程、测试水平的提高而不断下降,但无论如何,软件测试都是嵌入式软件
    的头像 发表于 11-24 16:18 499次阅读

    嵌入式C语言的结构特点

    嵌入式开发既有底层硬件的开发又涉及上层应用的开发,即涉及系统的硬件和软件,C语言既具有汇编
    的头像 发表于 11-24 16:16 631次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>C</b><b class='flag-5'>语言</b>的结构特点

    C语言进阶之嵌入式系统高级C语言编程

    电子发烧友网站提供《C语言进阶之嵌入式系统高级C语言编程.rar》资料免费下载
    发表于 11-18 10:32 1次下载
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>进阶之<b class='flag-5'>嵌入式</b>系统高级<b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>编程</b>

    c语言嵌入式开发

    电子发烧友网站提供《c语言嵌入式开发.zip》资料免费下载
    发表于 11-17 14:11 2次下载
    <b class='flag-5'>c</b><b class='flag-5'>语言</b><b class='flag-5'>嵌入式开发</b>

    嵌入式开发学习路线

    电子发烧友网站提供《嵌入式开发学习路线.doc》资料免费下载
    发表于 11-17 10:13 13次下载
    <b class='flag-5'>嵌入式开发</b>学习路线