C语言编程建议和技巧(同样适用于单片机的学习)_单片机c语言编程的高级技巧

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

+ [8 函数指针](#8__112)
+ [9 文件包含](#9__122)
+ [延伸阅读](#_129)
+ [下面是一些个人建议:](#_136)

我们鼓励在编程时应有清晰的哲学思维,而不是给予硬性规则。我并不希望你们能认可所有的东西,因为它们只是观点,观点会随着时间的变化而变化。可是,如果不是直到现在把它们写在纸上,长久以来这些基于许多经验的观点一直积累在我的头脑中。因此希望这些观点能帮助你们,了解如何规划一个程序的细节。(我还没有看到过一篇讲关于如何规划整个事情的好文章,不过这部分可以是课程的一部分)要是能发现它们的特质,那很好;要是不认同的话,那也很好。但如果能启发你们思考为什么不认同,那样就更好了。在任何情况下,都不应该照搬我所说的方式进行编程;要用你认为最好的编程方式来尝试完成程序。请一以贯之而且毫不留情的这么做。

1 排版问题

程序是一种出版物。意味着程序员们会先阅读(也许是几天、几周或几年后的你自己阅读),最后才轮到机器。机器的快乐就是程序能编译,机器才不在乎程序写的有多么漂亮,可是人们应该保持程序的美观。有时人们会过度关心:用漂亮的打印机呆板地打印出漂亮的输出,而这些输出只是将所有介词用英文文本以粗体字体凸显出来,都是些与程序无关的细节。虽然有很多人认为程序就应该像 Algol.68 所描述的一样(有些系统甚至要求照搬该风格编写程序),可清晰的程序不会因为这样的呈现而变得更清晰,只会使糟糕的程序变得更可笑。

对于清晰的程序来说,排版规范一向都是至关重要的。当然,众所周知最有用的是缩进,但是当墨水遮盖了意图时,就会控制住排版。因此即便坚持使用简单的旧打字机输出,也该意识到愚蠢的排版。避免过度修饰,比如保持注释的简洁和灵活。通过程序整齐一致地说出想表达的。接着往下看。

2 变量命名

对于变量名称,长度并不是名称的价值所在,清晰的表达才是。不常用的全局变量可能会有一个很长的名称,像 maxphysaddr。在循环中每一行所使用的数组索引,并不需要取一个比 i 更详尽的名字。取 index 或者 elementnumber 会输入更多的字母(或调用文本编辑器),并且会遮盖住计算的细节。当变量名称很长时,很难明白发生了什么。在一定程度上,这是排版问题,看看下面:

for(i=0 to 100)    vs    for(elementnumber=0 to 100)
    array[i]=0;              array[elementnumber]=0;

现实例子中的问题会变得更糟。所以仅需把索引当成符号来对待。
指针也需要合理的符号。np 仅仅只是作为指针 nodepointer 的助记符。如果一贯都遵从命名规范,那么很容易就能推断出 np 表示“节点指针”。在下一篇文章中会提到更多。

同时在编程可读性的其它方面,一致性也是极其重要的。假使变量名为 maxphysaddr,则不要给同级关系的变量取名 lowestaddress

最后,我倾向于 「最小长度」但「最大信息量」 的命名,并让上下文补齐其余部分。例如:全局变量在使用时很少有上下文帮助理解,那么它们的命名相对而言更需要令人易懂。因此我称 maxphyaddr 作为一个全局变量名,对于在本地定义和使用的指针来说 np 并不一定是 NodePoint。这是品味的问题,但品味又与清晰度相关。

我避免在命名时嵌入大写字母;它们的阅读舒适性太别扭了,像糟糕的排版一样令人心烦。

3 指针的使用

C语言不同寻常,因为它允许指针指向任何事物。指针是锋利的工具,像任何这样的工具一样,使用得当可以产生令人愉悦的生产力,但使用不当也可以造成极大的破坏。指针在学术界的名声不太好,因为它太危险了,莫名其妙地就变得糟糕的不行。但我认为它是强大的符号,它可以帮助我们清楚地自我表达。

思考:当有指针指向对象时,对于那个对象,确切地说它只是名称,其它什么也不是。听起来很琐碎,但看看下面的两个表达式:

np 
node[i]

第一个指向一个 node(节点),第二个计算为(可以说)同一个 node。但第二种形式是不太容易理解的表达式。这里解释一下,因为我们必须要知道 node 是什么,i 是什么,还要知道 inode 与周围程序之间相关的规则是什么。孤立的表达式并不能说明 inode 的有效索引,更不用提是我们想要元素的索引。

如果 ijk 都是 node 数组中的索引将很容易出差错,而且连编译器都不能帮助找出错误。当给子程序传参数时,尤其容易出错:指针只是一个单独的参数;但在接收的子程序中必须认为数组和索引是一体的。

计算为对象表达式本身,比该对象的地址更不易察觉,而且容易出错。正确使用指针可以简化代码:

parent->link[i].type    vs    lp->type.

如果想取下一个元素的 type 可以是

parent->link[++i].type   或   (++lp)->type.

i 前移,但其余的表达式必须保持不变;用指针的话,只需要做一件事,就是指针前移。

把排版因素也考虑进来。对于处理连续的结构体来说,使用指针比用表达式可读性更好:只需要较少的笔墨,而且编译器和计算机的性能消耗也很小。与此相关的问题是,指针类型会影响指针正确使用,这也就允许在编译阶段使用一些有用的错误检测,来检查数组序列不能分开。而且如果是结构体,那么它们的标签字段就是其类型的提示。因此:

np->left    是足以让人明白的。

如果是索引数组,数组将取一些精心挑选的名字,而且表达式也会变得更长:

node[i].left.    此外,由于例子变得越来越大,额外的字符更加让人恼火。

一般来说,如果发现代码中包含许多相似并复杂的表达式,而且表达式计算为数据结构中的元素,那么明智地使用指针可以消除这些问题。考虑一下:

if(goleft)
   p->left=p->right->lef;
else
   p->right=p->left->right;

看起来像利用复合表达式表示 p。有时这值得用一个临时变量(这里的 p)或者把运算提取成一个宏。

4 过程名称

过程名称应该表明它们是做什么的,函数名称应该表明它们返回什么。函数通常在像 if 这样的表达式使用,因此可读性要好。

if(checksize(x))   
是没有太大帮助的,因为不能推断出 checksize 错误时返回 true,还是非错误时返回。相反:
if(validsize(x))
使这点能清晰表达,并且在常规使用中将来也不大可能出错。

5 注释

这一个微妙的问题,需要自己体会和判断。由于一些原因,我倾向于宁可清除注释。第一,假如代码清晰,并且使用了规范的类型名称和变量名称,应该从代码本身就可以理解。第二,编译器不能检查注释,因此不能保证准确,特别是代码修改过以后。误导性的注释会非常令人困惑。第三,排版问题:注释会使代码变得杂乱。

但有时我会写注释,像下文一样仅仅只是把它们用于介绍。例如:解释全局变量的使用和类型(我总是在庞大的程序中写注释);作为一个不寻常或者关键过程的介绍;或标记出大规模计算的一节。

有一个糟糕注释风格的例子:

i=i+1;   /\* Add one to i \*/

还有更糟糕的做法:

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\* \*
\* Add one to i \*
\* \*
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
i=i+1;

先不要嘲笑,等到在现实中看到再去吧。

或许除了诸如重要数据结构的声明(对数据的注释通常比对算法的更有帮助),这样至关重要部分之外,需要避免对注释的“可爱”排版和大段的注释;基本上最好就不要写注释。如果代码需要靠注释来说明,那最好的方法是重写代码,以便能更容易地理解。这就把我们带到了复杂度。

6 复杂度

许多程序过于复杂,比需要有效解决的问题更加复杂。这是为什么呢?大部分是由于设计不好,但我会跳过这个问题,因为这个问题太大了。然而程序往往在微观层面就很复杂,有关这些可以在这里解决。

  • 规则 1:不要断定程序会在什么地方耗费运行时间。瓶颈总是出现在令人意想不到的地方,直到证实瓶颈在哪,不要试图再次猜测并加快运行速度。
  • 规则 2:估量(measure) 在没有对代码做出估量之前不要优化速度,除非发现最耗时的那部分代码,要不也不要去做。
  • 规则 3:当 n 很小时(通常也很小),花哨的算法运行很慢。花哨算法有很大的常数级别复杂度。在你确定 n 总是很大之前, 不要使用花哨算法。(即使假如 n 变大,也优先使用规则 2).例如,对于常见问题,二叉树总比伸展树高效。

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值