c语言是当今嵌入式开发中常见的语言,它是一种相对“低级”的高级语言,让嵌入式程序员能直接访问物理地址、操作硬件底层的同时,又能享受高级语言带来的便利。但c语言灵活的编程方式和语法规则,使其成为一门难以掌握的编程语言,在编写出的代码中,可能不可避免地存在一些错误。
在对安全性要求很高的领域,如轨交、汽车等,开发过程中必须考虑代码的安全性,这些领域代码的正确性会对人的生命产生决定性影响。那如何提高代码的安全性呢?有一句老话这么说:“无规矩不成方圆”,若我们有久经实践检验的正确规则,它给所有开发人员提供一些建议,告诉他们好的编程习惯和编程思路,让他们逐渐摒弃可能存在风险的编程行为,最终可以编写出更安全、更健壮的代码。
那么问题又来了,我们应该遵循哪些“规矩”?在这里向大家介绍由nasa 喷气推进实验室首席科学家gerard j. holzmann侧重于编码安全考虑,总结出的10条编写c代码编写的“黄金法则”,这10条法则足够精简又足够具体,能更好地被理解和记忆。它回答了我们在编写代码时的两个基本问题:代码的结构如何安排?应当或不应当使用哪些语言特性?为 nasa 工作的全球程序员遵循“黄金法则”,编写高度安全、能上天的代码。话不多说,我们先来看看这10条“黄金法则”:
• 简化控制流程
• 为循环使用固定次数上限
• 不使用动态内存分配
• 不使用冗长的函数
• 低断言密度
• 以最小范围级别声明数据对象
• 检查参数和返回值
• 限制预处理程序的使用
• 限制指针的使用
• 编译所有代码
我们从中摘出6条规则进行说明:
1. 简化控制流程,要求使用尽可能精简的控制流程构造编写程序,不要使用setjmp或longjmp、goto语句,以及直接或间接的递归。这样编写出的代码有助于结构清晰,增强了代码可验证能力。不使用递归,便不会产生循环的函数调用图,这样也可证明所有本应有界的执行实际上都是有界的。
2. 为循环使用固定次数上限,要求所有循环次数必须有固定的上限。可使用验证工具静态地证明,为循环中迭代数量所设立的上限次数未被超越。如果无法以静态方式对循环的次数界限加以证明,则可认为未遵守该原则。有助于预防代码失控。
3. 不使用动态内存分配,要求不在初始化完成后进行动态内存分配,因为malloc等内存分配机制,以及垃圾回收器可能会产生无法预知的行为,进而可能会对性能产生影响。更重要的是,还有可能因为程序员的失误造成内存错误,如试图分配超过可用物理内存数的内存、忘记释放内存、继续使用已被释放的内存、对已分配内存进行越界使用等。
4.不使用冗长的函数,要求任何函数的长度不应超过使用标准参考格式。过长的函数通常意味着结构并非最优。每个函数都应是可理解且可验证的单一逻辑单位。如果在计算机显示器上需要多屏界面才能完整显示,这样的逻辑单位通常会极难理解。
5. 以最小范围级别声明数据对象,该原则同时也是数据隐蔽的基本原则,所有数据对象均必须以尽可能最小的范围级别进行声明。
6. 限制指针的使用,指针的使用必须加以限制。通常只允许不超过一层的解引用,指针解引用操作不应隐藏在typedef声明或宏定义内部,此外函数指针也是不允许使用的。
nasa对这些原则的看法为:“这些原则就如同汽车安全带,也许一开始会觉得有些不舒适,但很快会变成每个人的第二本能,到时候很难想象会有人不这么做。”
随着技术的发展,嵌入式软件规模迅速增长,可靠性和安全性要求也越来越高,各行业陆续已发布规范文档,如iso26262功能安全标准等,均对软件的提出要求,在我们的开发中,即可遵循行业标准,开发出更安全、更可靠的软件。