November 06, 2006

避免寫出釀造災難的 C/C++ 程式

Embedded.com 刊載 Jack Ganssle 的文章 [MISRA minimizes mishaps],淺顯易懂但又讓人不得不重視 C/C++ 程式設計的細節。文章以 [International Obfuscated C Code Contest] 2004 年得獎者的「傑作」作為「難以維護的程式碼」範例,因為文章排版不甚清楚,這裡重新引用如下:[出處]
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
     
    #define _			;double
    
    #define void			x,x
    #define case(break,default)	break[O]:default[O]:
    
    #define switch(bool) 		;for(;x<bool;
    #define do(if,else)		inIine(else)>int##if?
    
    #define true			(--void++)
    #define false			(++void--)
     
    char*O=" <60>!?\\\n"_ doubIe[010]_ int0,int1 _ Iong=0 _ inIine(int eIse){int
    
    O1O=!O _ l=!O;for(;O1O<010;++O1O)l+=(O1O[doubIe]*pow(eIse,O1O));return l;}int
    main(int booI,char*eIse[]){int I=1,x=-*O;if(eIse){for(;I<010+1;I++)I[doubIe-1]
    =booI>I?atof(I[eIse]):!O switch(*O)x++)abs(inIine(x))>Iong&&(Iong=abs(inIine(x
    )));int1=Iong;main(-*O>>1,0);}else{if(booI<*O>>1){int0=int1;int1=int0-2*Iong/0
    [O]switch(5[O]))putchar(x-*O?(int0>=inIine(x)&&do(1,x)do(0,true)do(0,false)
    
    case(2,1)do(1,true)do(0,false)6[O]case(-3,6)do(0,false)6[O]-3[O]:do(1,false)
    
    case(5,4)x?booI?0:6[O]:7[O])+*O:8[O]),x++;main(++booI,0);}}}
果然讓人深感挫折,無法理解這其實是個多項式描繪製圖程式,拜讀的同時,不禁脫口而出「此曲只應天上有,人間哪得幾回聞」,凡人如我者,還是安分寫「老嫗皆解」的程式吧。

話是這麼說,不過即便「老嫗皆解」,還是可能錯誤百出。[MISRA - the Motor Industry Software Reliability Association] 針對產業界軟體可靠性與穩定度,提出一系列的方針與準則,而針對 C Programming Language 的部份則稱為 "MISRA-C"。[MISRA minimizes mishaps] 以 "MISRA-C" 的規範為基礎,標示以下五個原則:
  • C is incompletely specified. How does process(j++, j); behave? And exactly what is the size of an int? How astounding that such a basic component of any program is undefined!
  • Developers make mistakes, and the language does little to point out many of the even obvious screwups. It's so easy to mix up "=" and "==."
  • Programmers don't always have a deep knowledge of the language and so make incorrect assumptions. Compilers have bugs, or purposely deviate from the ANSI standard. Most 8051 compilers, for instance, have run-time packages that take and return single precision results for trig functions instead of the prescribed doubles.
  • C offers little intrinsic support for detecting run-time errors.
當然,上述的議題不全然只是「原則」,也可用以解釋為何我們所用的軟體充滿陷阱、安全性漏洞,或者致命的地雷。MISRA-C 羅列若干項概念性的條款,建議程式設計者應該留意,不過有些概念則有待商榷或釐清,比方說:
    * Rule 20.4: Dynamic heap memory allocation shall not be used.
啊?那我們該如何表示一些常見的資料結構或演算法呢?Jack Ganssle 做了補充:
    This rule also specifically outlaws the use of library functions that dynamically allocate memory. Admittedly, malloc() and its buddies can wreak havoc on deterministic systems that never get cleaned up via a reboot. But it's a whole lot of work to recode libraries that employ these common functions if you even know how the library routines work. What fun we might have stripping all of the malloc()s and free()s from embedded Linux! That would be a jobs-creation program for programmers. Let's see if we can get it stashed in amongst the other 15,000 items of pork Congress awards to their supporters each year.
避免釀造巨大的災難,最好的方法就是防範於未然。
由 jserv 發表於 November 6, 2006 08:53 PM
迴響

FYI: 老嫗能解 :P

duan 發表於 November 7, 2006 09:49 AM