Chinaunix

标题: mirsa c 要求付费的c规范 [打印本页]

作者: odin_free    时间: 2003-07-01 22:04
标题: mirsa c 要求付费的c规范
MISRA--作为工业标准的C编程规范(1)

[ 作者: myan   添加时间: 2003-1-8 19:35:44 ]
     
      MISRA (The Motor Industry Software Reliability Association 汽车工业软件可靠性联会) 是位于英国的一个跨国汽车工业协会,其成员包括了大部分欧美汽车生产商。其核心使命是为汽车工业提供服务和协助,帮助厂方开发安全的、高可靠性的嵌入式软件。这个组织最出名的成果是所谓的MISRA C Coding Standard,这一标准中包括了127条C语言编码标准,通常认为,如果能够完全遵守这些标准,则你的C代码是易读、可靠、可移植和易于维护的。最近很多嵌入式开发者都以MISRA C来衡量自己的编码风格,比如著名的uC/OS-II就得意地宣称自己99%遵守MISRA标准。而《嵌入式开发杂志》也专门载文号召大家学习。编码规范通常是一个公司自定的“土政策”,居然有人去做标准,而且还得到广泛的认可,这不禁引起我强烈的兴趣。可惜这份标准的文本需要花钱去买,而且短短几十页,要价非常昂贵。MISRA在网上公布了一些文档,其中有关于MISRA C Coding Standard的Clarification报告,从中间你可以大致猜到MISRA标准本身是什么。我仔细阅读了这些文档,并且通过阅读其他一些介绍性文档,大致了解了MISRA标准的主要内容。这些条款确有过人之处,对于C/C++语言工程项目的代码质量管理能够起到良好的指导性作用,对于大部分软件开发企业来说,在MISRA的基础上适当修改就可以形成自己的规范。当然其中也有一些过于严苛的东西,这就需要各个开发部门灵活处理了。我个人的体会,编码规范虽然很简单,但是要完全执行,不折不扣,需要开发部门有很高的组织性和纪律性,并且有很好的代码评审机制。因此,如果能够严格地遵守编码规范,本身就是一个开发部门实力的证明。

这里不可能将所有规则一一列出(事实上正式文本我一条也没看到),只列出一些比较有意思的条款,让大家有机会了解MISRA的风格。具体的内容,感兴趣的朋友可以自己到www.misra.org.uk去了解。
  1. Rule 1. 严格遵循ANSI C89标准,不允许任何扩展。

  2. Rule 3. 如果要嵌入汇编语言,则必须将所有汇编语句包装在C函数里,而且这些函数中只有汇编语句,没有常规C语句。
  3.    
  4. Rule 7. 不得使用三元操作符(? : )

  5. Rule 10. 不得残留被注释掉的废代码。

  6. Rule 11. 所有标识符不超过31字符。

  7. Rule 12. 不同名空间中的变量名不得相同。
  8.         例如:
  9.             typedef struct MyStruct {... } MyStruct;  (违规)

  10.             struct Person {
  11.               char* name;
  12.               ...
  13.             };

  14.             char name[32];  (违规)

  15. Rule 13. 不得使用char, int, float, double, long等基本类型,应该用自己定义的类型显示表示类型的大小,如CHAR8, UCHAR8, INT16, INT32, FLOAT32, LONG64, ULONG64等。

  16. Rule 14. 不得使用类型char,必须显示声明为unsigned char或者signed char。

  17. Rule 18. 所有数字常数应当加上合适的后缀表示类型,例如51L, 42U, 34.12F等。

  18. Rule 19. 禁止使用八进制数。(因为086U这样的常数很容易引起误解)。

  19. Rule 21. 不得定义与外部作用域中某个标识符同名的对象,以避免遮盖外部作用域中的标识符。

  20. Rule 23. 具有文件作用域的对象尽量声名为static的。

  21. Rule 24. 在同一个编译单元中,同一个标识符不应该同事具有内部链接和外部链接的声名。

  22.         这里我略作说明:
  23.       
  24.         我们通常将一些放在头文件里的变量声名为“外部链接”的,如:
  25.         extern UINT32 g_count;  // 俗话叫变量声明(对应于变量定义,不分配实际空间)

  26.         对于“使用”这个变量的.c文件来说,这很好,因为g_count始终保持外部链接性质。可是对于定义g_count(实际分配空间)的.c文件来说,如果包含了上述的头文件,则在这个编译单元里就发生了内部链接和外部链接的冲突。解决办法是,定义g_count的文件尽量不要包含声名g_count的头文件。个人感觉这不是任何时候都做得到的,尤其是在对付遗留代码的时候。

  27. Rule 25. 具有外部链接性质的标识符应该只声明一次。

  28. Rule 27. 外部对象不得在多个文件中声名。

  29. Rule 28. 禁止使用register关键字。

  30. Rule 29. 自动对象(栈对象)使用前必须赋初值。

  31. Rule 33. 操作符&&和||的右侧表达式不得具有副作用(side-effect)。
  32.         也就是说,象 if (x == 20 && ++y == 19)这样的表达式被禁止。

  33. Rule 35. 在返回布尔值的表达式中不得出现赋值操作。
  34.         也就是说,我们常用的 if (!(fp = fopen("fname", "r"))) { /* error */ }
  35.         被禁止。

  36. Rule 37. 不得对有符号数施加位操作,例如 1 << 4 将被禁止,必须写 1UL << 4;

  37. Rule 39. 不得对有符号表达式施加一元 "-" 操作符。

  38. Rule 40. 不得对有副作用的表达式施加sizeof操作符。

  39. Rule 42. 除了循环控制语句,不得使用逗号表达式。

  40. Rule 44. 禁止冗余的显式转型。比如: double pi = (double) 3.1416F;

  41. Rule 45. 禁止从任意类型到指针的强制转型,禁止从指针到任意类型的强制转型。
  42.         例如:void* p = (void*)0xFFFF8888UL;

  43. Rule 49. 显示测试值是否为零。

  44. Rule 50. 不得显式判断浮点数的相等性和不等性。

  45. Rule 52. 不得遗留“永远不会用到”的代码。

  46. Rule 53. 所有非空语句必须具有副作用。

  47. Rule 55. 除了switch语句,不得使用标号(label)。

  48. Rule 56. 不得使用goto.

  49. Rule 57. 不得使用continue。

  50. Rule 58. 除了switch语句,不得使用break.

  51. Rule 59. if, else if, else, while, do..while, for语句块必须使用{}括起。

  52. Rule 60. 任何if..else if 语句,最后必须有一个收尾的else。例如:
  53.         if (ans == 'Y') {
  54.           ...
  55.         }
  56.         else if (ans == 'N') {
  57.           ...
  58.         }
  59.         else if (ans == 'C') {
  60.           ...
  61.         }
  62.         else {
  63.           ;
  64.         }

  65. Rule 67. 循环计数器的值不得在循环体内修改。

  66. Rule 70. 禁止任何直接和间接的递归函数调用。

  67. Rule 82. 每个函数只能有一个推出点。

  68. Rule 86. 如果一个函数可能返回错误信息,则调用后必须加以测试。

  69. Rule 92. 不应该使用#undef

  70. Rule 95. 不得将宏作为参数传给宏函数

  71. Rule 98. 在一个宏定义中,#或##符号只能出现一次。

  72. Rule 101. 禁止指针运算(代之以数组下标运算)。

  73. Rule 102. 禁止超过两级的指针。

  74. Rule 104. 禁止使用指向函数的非常量指针。

  75. Rule 106. 不得将栈对象的地址传给外部作用域的对象。

  76. ********************************************************************
  77. 后面的规则针对实时嵌入式系统,对其他类型的开发未必适用,如:

  78. Rule 118. 禁止使用动态堆分配(也就是不得使用malloc, calloc和realloc)。

  79. Rule 119. 禁止使用errno。

  80. Rule 120. 禁止使用offsetof.

  81. Rule 121. 禁止使用<locale.h>;

  82. Rule 122. 禁止使用setjmp, longjmp.

  83. Rule 123. 禁止使用<signal.h>;

  84. Rule 124. 禁止使用<stdio.h>;(不能用printf, scanf了!)

  85. Rule 125. 禁止使用atoi, atof, atol。(这个我很赞成,建议使用strtol, strtod等函数)

  86. Rule 126. 禁止使用abort, exit, getenv。

  87. Rule 127. 禁止使用<time.h>;
复制代码


从此更加完善我们的风格吧~
作者: 无双    时间: 2003-07-01 22:11
标题: mirsa c 要求付费的c规范
Rule 7. 不得使用三元操作符(? : )

Rule 10. 不得残留被注释掉的废代码。

FT
三无我喜欢用在返回值中 因为比较简单

注释掉的代码有时不能很肯定或是以后还会有参考价值的话

那么也会保留在源程序中的
但DEBUG或是ifdef 这类很少在完成后的代码中
作者: odin_free    时间: 2003-07-01 22:16
标题: mirsa c 要求付费的c规范
原帖由 "无双" 发表:
Rule 7. 不得使用三元操作符(? : )

Rule 10. 不得残留被注释掉的废代码。

FT
三无我喜欢用在返回值中 因为比较简单

注释掉的代码有时不能很肯定或是以后还会有参考价值的话

那么也会保留在源程序中?.........
  

我觉得(?: )这个还好些
注释代码可以用 if(0){}以后条是可以用
在linux内核里很多这么用的
直接注释感觉太粗暴,看起来也不美观
作者: 无双    时间: 2003-07-01 22:20
标题: mirsa c 要求付费的c规范
我那一般直接注释

看起来是不美观
但是如果使用if(0)注释的代码太多
后面添加的代码很多的话
看起来也会很累

因为太长了

不过你的贴子还是很有价值的
加个精华
作者: odin_free    时间: 2003-07-01 22:41
标题: mirsa c 要求付费的c规范
对~这个帖子 我一看就收了

以前一直没有系统的好的风格选择 各种风格基本是支离破碎

这个算是一个比较完善的体系

注:第一个精华,开心
作者: gadfly    时间: 2003-07-01 23:00
标题: mirsa c 要求付费的c规范
大部分都同意和遵循,33,59,是需要注意的规则。但是有两个值得商榷,

例如:
Rule 58. 除了switch语句,不得使用break.
Rule 101. 禁止指针运算(代之以数组下标运算)。

break在循环中使用的话,能是代码简洁,嵌套不至于太深。
指针运算也是,适当的应用,代码其实很清爽。

至于3元,我觉得一层还可以,多层就是卖弄技巧了,只能让后来的人看的头晕。

至于注释无用代码,我觉得如果是项目中使用版本控制工具,无用的代码完全可以去掉,当然如果是很简单的程序,可以用if 方式取消作用。
作者: 无双    时间: 2003-07-01 23:03
标题: mirsa c 要求付费的c规范
三层多层的话确实是难理解
另外指针现在已习惯

当然使用数数组的方法也是OK D
但这种情况下*a++
写成数组需要添加多代码
先是变量定义
然后是两个操作
效率会降低
作者: 蓝色键盘    时间: 2003-07-02 11:09
标题: mirsa c 要求付费的c规范
如下的规则,个人觉得不太合适。


  1.    
  2. Rule 7. 不得使用三元操作符(? : )

  3. Rule 13. 不得使用char, int, float, double, long等基本类型,应该用自己定义的类型显示表示类型的大小,如CHAR8, UCHAR8, INT16, INT32, FLOAT32, LONG64, ULONG64等。

  4. Rule 42. 除了循环控制语句,不得使用逗号表达式。


  5. Rule 55. 除了switch语句,不得使用标号(label)。

  6. Rule 56. 不得使用goto.

  7. Rule 57. 不得使用continue。

  8. Rule 58. 除了switch语句,不得使用break.

  9. Rule 67. 循环计数器的值不得在循环体内修改。

  10. Rule 70. 禁止任何直接和间接的递归函数调用。

  11. Rule 92. 不应该使用#undef

复制代码


Rule 101. 禁止指针运算(代之以数组下标运算)。
作者: 蓝色键盘    时间: 2003-07-02 11:12
标题: mirsa c 要求付费的c规范
不过如下的规则非常好


  1. 如果一个函数可能返回错误信息,则调用后必须加以测试。
复制代码

作者: ffaatt    时间: 2003-07-02 11:18
标题: mirsa c 要求付费的c规范
规则总是要以牺牲效率和简洁为代价的。
再说一个程序里需要用到高效率代码的地方最多不过几行而已。

如果大家写程序都能忘记所谓的简洁、高效,那是多么理想的境界啊。

软件开发应该也有类似TCO的概念,编码时牺牲的效率会在调试、维护时得到补偿。
作者: gadfly    时间: 2003-07-02 12:27
标题: mirsa c 要求付费的c规范
我觉得是不能照搬。

57,58是一类。在循环中应该说是必不可少的。

70可能是指不许递归相互调用吧。

其实有的简化,也有利于维护,当然让人易于迷惑的除外。让你一目了然知道代码是在干什么。
作者: 蓝色键盘    时间: 2003-07-02 12:36
标题: mirsa c 要求付费的c规范
设想一下。对于while(1)或者for(;
如果break和continue和goto不用,一些逻辑很难实现的。
作者: ffaatt    时间: 2003-07-02 14:29
标题: mirsa c 要求付费的c规范
break和continue相当于goto,所以建议不用。
这个规范就是不让你写类似:while(1)或者for(;
作者: tinywind    时间: 2003-07-02 14:57
标题: mirsa c 要求付费的c规范
不准用goto如何做到函数单点退出?
作者: 无双    时间: 2003-07-02 15:04
标题: mirsa c 要求付费的c规范
if else啊

但这样一来程序会变得很复杂

所以我觉得还是使用的好

另外这只是他的建议
其它公司最后使用时也会变通的
没有看到vxworks上也写着才99%吗
作者: unicorns    时间: 2003-07-02 16:26
标题: mirsa c 要求付费的c规范
Rule 57. 不得使用continue。

Rule 58. 除了switch语句,不得使用break.

Rule 67. 循环计数器的值不得在循环体内修改。

Rule 70. 禁止任何直接和间接的递归函数调用。

Rule 82. 每个函数只能有一个推出点。

Rule 101. 禁止指针运算(代之以数组下标运算)。

这几条太狠了吧
有几个人可以做到?
作者: odin_free    时间: 2003-07-02 19:48
标题: mirsa c 要求付费的c规范
这个是有所得 必有所失
规矩多了 自然代码规范
但是设计的灵活就受到限制
作者: JohnBull    时间: 2003-07-02 20:12
标题: mirsa c 要求付费的c规范
不让用递归太过分了吧?
作者: 无双    时间: 2003-07-03 13:15
标题: mirsa c 要求付费的c规范
递归层数过多的话容易会堆栈溢出

但是如果肯定层数很少
那么使用没有什么问题吧

我觉得使用它做某些操作还是很简单的
如树的遍历
作者: ffaatt    时间: 2003-07-03 14:21
标题: mirsa c 要求付费的c规范
这个规范恐怕主要是用于底层开发的啊,汽车行业/vxwork...
这种情况下规范苛刻一些是正常的,也不会有多少机会用到递归,大家不做嵌入式系统就不需要严格照搬,否则就矫枉过正了,但是体会一下苛刻的规范还是有益的。
当年我做嵌入式开发的时候,好几次不得不把整个系统代码费掉重来,就是因为自己没有能力事先建立一套规范,当时要是先看到这个规范该多好啊,不过没有吃过苦头的人未必理解这个规范的价值。
就想楼主说的,制作规范体现实力,遵守规范更体现实力。
作者: ffaatt    时间: 2003-07-03 14:34
标题: mirsa c 要求付费的c规范
嵌入式开发的技术未必复杂,除了个别核心算法外。
  关键是代码高度规范来保证可靠性和可移植性。一套代码要同时在几种不同的CPU、不同外设配置上测试、运行,绝大多数时间不是在编码,而是在交叉编译、在线调试、测试。根本没心情追求代码简介、漂亮,99%的代码也和执行效率无关。
  真正要求高效的代码实际上是靠专业级的编译器优化得到的,因为要精确计算每条指令的CPU周期和流水线堵塞情况,程序员只是作一些微调而已。
  有时候碰到CPU、存储器条件苛刻时,确实也顾不上规范,为了减少一个字节或一个指令周期,代码写的极难看。还好大多数时候不必这样,所以vxworks可以99%遵守规范。
作者: fr21cn    时间: 2003-07-03 21:01
标题: mirsa c 要求付费的c规范
如何付费???
作者: odin_free    时间: 2003-07-03 21:26
标题: mirsa c 要求付费的c规范
原帖由 "无双" 发表:
递归层数过多的话容易会堆栈溢出

但是如果肯定层数很少
那么使用没有什么问题吧

我觉得使用它做某些操作还是很简单的
如树的遍历
   

递归的好处是思路简洁 代码清晰
缺点是用堆栈 浪费资源
我个人感觉可以用递归把解决思路搞通
然后用非递归实现
作者: fsb    时间: 2003-07-04 21:45
标题: mirsa c 要求付费的c规范
看了其它的文章有用栈消除递归的说法.
不知有没有这方面的例子
作者: 无双    时间: 2003-07-04 22:13
标题: mirsa c 要求付费的c规范
一般是使用循环展开的方法

理论上多数多数是可以使用其它方法代替的
除了一些特殊要求
如非波那序列

每一个值都依赖前一个值的结果

F(0) =1 F(1)=1
F(N)=F(N-1)+F(N-2)
作者: 强人    时间: 2003-07-06 16:33
标题: mirsa c 要求付费的c规范
意义不大




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2