Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C Pitfalls and Ada benefits

C Pitfalls and Ada benefits

Modified presentation to quickly cover the Ada language during one of the Lunch & Learn sessions in one of my contractors.

The focus is on how the Ada language and the way to express code can prevent common errors within critical systems.

Francisco Garcia

April 18, 2010
Tweet

Other Decks in Technology

Transcript

  1. C pitfalls and Ada benefits Francisco García Rodríguez <[email protected]> Adaptation

    from a previous work from: Franco Gasperoni <[email protected]> http://libre.adacore.com June 2010
  2. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Copyright

    Notice © AdaCore under the GNU Free Documentation License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Fr Software Foundation; provided its original author is mention and the link to http://libre.act-europe.fr/ is kept. A copy of the license is included in available at: http://www.fsf.org/licenses/fdl.html
  3. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Choosing

    the right language I speak Spanish to God, Italian to women, French to men and German to my horse (Charles V ) In Software Engineering, different programming languages are also better suited for different types of tasks
  4. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Choosing

    the right language There is no language better for everything ... however ... The best choice is usually ignored because of the marketing and popularity of mainstream languages Microsoft promoted C++, IBM/Sun made the same for Java. That is the main reason for which these languages are common, not because they are specially good ones
  5. http://libre.adacore.com © AdaCore under the GNU Free Documentation License The

    Productivity Point of View Scripting: Higher Level Programming for the 21st Century John K. Ousterhout Language Level Relative to C C 1 C++ 2.5 Fortran 2 Java 2.5 Perl 6 Python 6 Steven McConnell
  6. http://libre.adacore.com © AdaCore under the GNU Free Documentation License So...

    What is C good for? C is a low-level and powerful language that replaced assembly programming in the embedded market. Programmers can easily visualize the connection between one line of C and machine code. This abstraction is very convinient when a complete control of the code is needed. However it lacks many modern and useful constructs. C was not designed with the goal of supporting safety-critical applications: It has a variety of error-prone constructs such as confusing type conversion rules and an absence of array index checking that compromise reliability. In addition, loose type checking complicates analyzability (Ben Brosgol)
  7. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Important

    Note All C code examples showed in this lecture compile • Some are correct according to the C semantics • Others are specified to be errors in the standards but … • … an implementation is not required to detect them They contain coding errors undetected by most C comp Some errors will never be solved because of the languag design and ambiguities of the C standard This lecture is not an exhaustive list of pitfalls, there are • C Traps and Pitfalls by Andrew Koenig (Addison Wesley) • A critique of C++ (Ian Joyner)
  8. http://libre.adacore.com © AdaCore under the GNU Free Documentation License If

    Anybody Can't Read It Don't Write It A piece of code is written once …. … but read and modified many many times C syntax favors conciseness over readability • This leads to bugs and wasted time in debugging • This means that software is more costly to develop or is buggy or Ada syntax favors readability • Ada compiler catches silly mistakes • Faster and cheaper to produce correct code when written in Ada
  9. http://libre.adacore.com © AdaCore under the GNU Free Documentation License #include

    <stdio.h> int main () { int length = 8265; int width = 0252; int height = 8292; printf ("length = %d\n", length); printf ("width = %d\n", width); printf ("height = %d\n", height); } What is the Program Output ? This program compiles fine What is its output?
  10. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Inside

    Procedure Main with Text_IO; procedure Main is Length : Integer := 8265; Width : Integer := 0252; Height: Integer := 8292; begin Text_IO . Put_Line (Length'img); Text_IO . Put_Line (Width'img); Text_IO . Put_Line (Height'img); end Main; Statement origin is clear. Improved namespace eases code understanding No surprises. Numbers are interpreted just as we humans interpret them No surprises in Ada
  11. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Length

    : Integer := 8265; Width : Integer := 0252; -- regular decimal number Width_8 : Integer := 8#252#; -- octal number B_Mask : Integer := 2#1100_1011#; -- binary number W_Mask : Integer := 16#FFF1_A4B0#; -- Hexadecimal number -- Fixed point type for numbers from -999_999.999 to +999_999.999 type My_Float is delta 0.001 digits 9; Readability does matter In Ada you can specify any base from 2 to 16 (for integer and real numb Use the “_” to separate digits for clarity: 1_000_000_000 C is for embedded software but cannot recognize a simple binary numb
  12. http://libre.adacore.com © AdaCore under the GNU Free Documentation License #include

    <limits.h> void check_divide (int *k, int x, int *y) { if (*y != 0) if (x = 0) *k = INT_MAX; else *k = x / *y; *y++; } Is the Following Code Correct ? This program compiles fine, but has a number of problems. Which ones?
  13. http://libre.adacore.com © AdaCore under the GNU Free Documentation License #include

    <limits.h> void check_divide (int *k, int x, int *y) { if (*y != 0) { if ( x == 0) *k = INT_MAX; } else { *k = x / *y ; (*y)++; /* before was *(y++) */ } } The Correct Version
  14. http://libre.adacore.com © AdaCore under the GNU Free Documentation License procedure

    Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is begin if Y = 0 and then X > 0 then K := Integer’Last; -- K is set to the largest Integer elsif Y /= 0 then K := X / Y; Y := Y + 1; end if; end Check_Divide; Ada Solves all the Previous Pitfalls Did you notice that there is no need for the * operator?
  15. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Working

    with pointers In C many novice programmers work with pointers based on the approach of fixing compilation errors, without understanding what they are really writing. int a[10], b[10]; int *c = 0; memcpy (&a,b, sizeof(int)*10); memcpy (&a,&b, sizeof(int)*10); memcpy (c, *a, sizeof(int)*10); memcpy (&c, a, sizeof(int)*10);
  16. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Operator

    Precedence & Associativity means • It does NOT mean if ((x & mask) == 0) if (x & mask == 0) if (x & (mask == 0) ) means • It does NOT mean if ((x < y) && (y < z)) if (x < y < z) if (((x < y) && (1 < z)) || (0 < z)) This is not an exhaustive list. Examples • What does hi << 4 + low mean? • What does x == y == z mean?
  17. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Ada

    Solution • You have to write if x and mask = 0 … if (x and mask) = 0 … • You have to write if x < y < z … Gives you a compiler error in Ada Gives you a compiler error in Ada if (x < y) and (y < z) …
  18. http://libre.adacore.com © AdaCore under the GNU Free Documentation License //

    If the signal ahead is clear then increase the speed. void increase_speed_if_safe (int speed, int signal) { if (signal == CLEAR); increase_speed (); } Is the Following Code Correct ? This program compiles fine, but has a problem. Which one?
  19. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Bugs

    can Have Serious Consequences { if (signal == CLEAR); increase_speed (); }
  20. http://libre.adacore.com © AdaCore under the GNU Free Documentation License --

    If the signal ahead is clear then increase the speed. procedure increase_speed_if_safe (speed : integer; signal : integer) is begin if signal = CLEAR then increase_speed; end if; end increase_speed_if_safe; The Ada Version is Always Safe If you write if signal = CLEAR then ; • You get a compiler error
  21. http://libre.adacore.com © AdaCore under the GNU Free Documentation License More

    Bad Luck in C Enumerations and Switch Statements enum Alert_Type {LOW, MEDIUM, HIGH, VERY_HIGH}; void handle_alert (enum Alert_Type alert) { switch (alert) { case LOW: activate_camera (); case MEDIUM: send_guard (); case HIGH: sound_alarm (); } } void process_alerts () { handle_alert (2); … This program compiles fine, but has a number of problems. Which ones?
  22. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Defects

    in the Previous Code void handle_alert (enum Alert_Type alert) { switch (alert) { case LOW: activate_camera (); break; case MEDIUM: send_guard (); break; case HIGH: sound_alarm (); break; case VERY_HIGH: alert_police (); break; } } void process_alerts () { handle_alert (HIGH); Don't forget break statements C does not check that you have treated all cases in the switch case labels can be integers or (values of) any enum type, not just enum Alert_Type which in most cases will be an error
  23. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Ada

    is Safer (and Less Verbose) type Alert_Type is (LOW, MEDIUM, HIGH, VERY_HIGH); procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when HIGH => Sound_Alarm; when VERY_HIGH => Alert_Police; end case; end Process_Alert; No break statements Ada will check that you have treated all cases in the case statement You can only use an object of type Alert_Type
  24. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Combining

    Cases procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM .. VERY_HIGH => Send_Guard; Sound_Alarm; Alert_Police; end case; end Process_Alert; A range is a set of ordered values • MEDIUM .. VERY_HIGH = MEDIUM, HIGH, VERY_HIGH
  25. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Conciseness

    and Non Determinism What does the following mean: • It compiles, but … • … Its semantics are undefined It could mean any one of the following (when written in Ada): { int k = 0; int v [10]; k = v [k++]; } declare K : Integer := 0; V : array (0 .. 9) of Integer; begin K := V (K); … declare K : Integer := 0; V : array (0 .. 9) of Integer; begin K := V (K); K := K + 1; … declare K : Integer := 0; V : array (0 .. 9) of Integer; begin Erase_All_Hard_Disks; …
  26. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Always

    Favor Readability What does the following mean: • This is valid C • It is very concise … • … but not very readable What does the following mean: • This is valid Ada • It is less concise … • … but very readable int x; int y; int z; … x = y---z--; x : integer; y : integer; z : integer; … x := y - z; y := y - 1; z := z - 1;
  27. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Good

    Programming is Altruistic Other people read and work on your code Write your code so that others can read and work on it Readability is a function of the programmers in your g • Present AND future programmers If you are in a group of gurus it is ok to prefer In most organizations programmers are not gurus In this case is easier to read/work on x = y-- - z--; x = y - z; y--; z--;
  28. http://libre.adacore.com © AdaCore under the GNU Free Documentation License C

    Example The program to the left compiles f Is there anything wrong? typedef int Time; typedef int Distance; typedef int Speed; … const Speed SAFETY_SPEED = 120; … void increase_speed (Speed s); … void check_speed (Time t, Distance d) { Speed s = d/t; if (s < SAFETY_SPEED) increase_speed (t); } void perform_safety_checks () { Time t = get_time (); Distance d = get_distance (); … check_speed (d, t); }
  29. http://libre.adacore.com © AdaCore under the GNU Free Documentation License What's

    Wrong with C Program compiles fine but has 2 serious flaws that go undetected FLAW 1: • t is a Time • increase_speed() takes a Speed parameter • Time and Speed are conceptually different, they should not be mixed up FLAW 2: • Distance and Time parameters have been inverted • Time and Distance are conceptually different, they should not be mixed up C provides NO HELP to the programmer in detecting these mistakes typedef int Time; typedef int Distance; typedef int Speed; … const Speed SAFETY_SPEED = 120; … void increase_speed (Speed s); … void check_speed (Time t, Distance d) { Speed s = d/t; if (s < SAFETY_SPEED) increase_speed (t); } void perform_safety_checks () { Time t = get_time (); Distance d = get_distance (); … check_speed (d, t); }
  30. http://libre.adacore.com © AdaCore under the GNU Free Documentation License What

    About Ada? You can write the same buggy code in Ada, but … … Ada has two lines of defense that do not exist in C to protect the programmer • User defined types • Parameter associations -- Buggy code. DON'T write this SAFETY_SPEED : constant Integer := 120; … procedure Increase_Speed (S : Integer); … procedure Check_Speed (T : Integer; D : Integer) is S : Integer := D / T; begin if S < SAFETY_SPEED then Increase_Speed (T); end if; end Check_Speed; procedure Perform_Safety_Checks is T : Integer := Get_Time; D : Integer := Get_Distance; begin … Check_Speed (D, T); end Perform_Safety_Checks;
  31. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Ada

    is Strongly Typed (1 of 2) When you define the proper types the Ada compiler catches the errors To mix different types you must use explicit conversions in Ada D is of type Distance, T is of type Time, S is of type Speed • Only objects of the same type can be mixed together in this fashion Increase_Speed is expecting a Speed parameter not a Time Each user defined integer type introduces a new type This new type is NOT a synonym of Integer type Time is range 0 .. 3_600; type Distance is range 0 .. 1_000; type Speed is range 0 .. 4_000; SAFETY_SPEED : constant Speed := 120; procedure Increase_Speed (S : Speed); procedure Check_Speed (T : Time; D : Distance) is S : Speed := D / T; begin if S < SAFETY_SPEED then Increase_Speed (T); end if; end Check_Speed; … Compilatio n error
  32. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Ada

    is Strongly Typed (2 of 2) Parameters switched! Moral of the story: Strong typing can detect more human errors within the algorithm. C compilers only detect syntax errors. type Time is range 0 .. 3_600; type Distance is range 0 .. 1_000; type Speed is range 0 .. 4_000; … procedure Check_Speed (T : Time; D : Distance); … procedure Perform_Safety_Checks is T : Time := Get_Time; D : Distance := Get_Distance; begin … Check_Speed (D, T); end Perform_Safety_Checks; Compilation error
  33. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Ada

    has Named Parameters type A_Type is …; procedure Safe_Copy (Source : A_Type; Target : A_Type); procedure Try is X : A_Type := …; Y : A_Type := …; begin Safe_Copy (Source => X, Target => Y); … end Try; Named parameter
  34. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Arrays

    in C No real arrays in C An array is just a pointer to a chunk of memory #include <stdio.h> int main () { char *str = "bugy"; printf ("%c\n", 0 [str]); printf ("%c\n", * (str+1)); printf ("%c\n", * (2+str)); printf ("%c\n", str [3]); }
  35. http://libre.adacore.com © AdaCore under the GNU Free Documentation License procedure

    Compute (N : Integer) is type Arr is array (Integer range <>) of Float; A : Arr (1 .. N) := (others => 9); B : Arr := A; C : Arr (11 .. 20) := (1, 2, others => 0); begin C := A; C (15 .. 18) := A (5 .. 8); end Compute; Typed Arrays B takes its bounds from A If C'Length /= A'Length then Constraint_Error is raised If A'Last < 8 then Constraint_Error is raised
  36. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Arrays

    in Ada are Safe If you try to index a non-existent arry position, a Constraint_Error exception is raised procedure Checks is A : array (1 .. 100) of Integer; begin A (101) := 1; end Checks; Exception raised
  37. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Record

    Types type Date is record Day : Positive range 1 .. 31 := 1; Month : Positive range 1 .. 12 := 1; Year : Integer := 2000; end record; D : Date := (3, 9, 1975); A : Date := (Day => 31, Month => 12, Year => 1999); • Ada is more expresive • Structures can define default values! This is specially important when implementing tests and/or upgrading their definitions
  38. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Mapping

    structures Represent the following program status word (PSW): PSW is 24 bits Bits 0 to 7 contain a 7 bit system mask Bits 12 to 15 contain a 4 digit protection key Bits 20 to 24 contain the status flags of the machine for the program • The fours flags are called: A, M, W and P All other bits are unused System Mask unused Protection Key unused Machine State 0 7 8 11 12 15 16 19 20 23
  39. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Maping

    structures in Ada type State is (A, M, W, P); type Byte_Mask is array (0 .. 7) of Boolean; type State_Mask is array (State) of Boolean; type Program_Status_Word is record System_Mask : Byte_Mask; Protection_Key : Integer range 0 .. 15 := 12; Machine_State : State_Mask; end record; for Program_Status_Word use record System_Mask at 0 range 0 .. 7; Protection_Key at 0 range 12 .. 15; Machine_State at 0 range 20 .. 23; end record; for Program_Status_Word ' Size use 3 * System.Storage_Unit; System Mask unused Protection Key unused Machine State 0 7 8 11 12 15 16 19 20 23
  40. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Mapping

    structures in C #pragma pack(1) typedef struct { unsigned int System_Mask:8; unsigned int spare1:4; unsigned int Protection_Key:4; unsigned int spare2:4; unsigned int Machine_State:4; } Program_Status_Word; System Mask unused Protection Key unused Machine State 0 7 8 11 12 15 16 19 20 23 Is this the right mapping? Maybe! In the C standard, the default layout of a structure is not only not specified, but cannot be defined In practice it is a trial/error procedure since the layout is decided by every compiler, not the language itself
  41. http://libre.adacore.com © AdaCore under the GNU Free Documentation License Simple

    record procedure main is type T_Month is range 1 .. 12; type T_Day is range 1 .. 31; type Date is record Day : T_Day; Month : T_Month; Year : Positive; end record; D : Date; begin D := Date'(3, 9, 1975); end main; procedure main is type main__t_month is range 1 .. 12; type main__t_day is range 1 .. 31; type main__date is record day : main__t_day; month : main__t_month; year : positive; end record; [type main__Tt_dayB is new short_short_integer] freeze main__Tt_dayB [] freeze main__t_day [] [type main__Tt_monthB is new short_short_integer] freeze main__Tt_monthB [] freeze main__t_month [] freeze main__date [] d : main__date; begin d := main__date'(( day => 3, month => 9, year => 1975)); return; end main;
  42. http://libre.adacore.com © AdaCore under the GNU Free Documentation License "Ada

    Can Call C" You can call C routines from Ada code -- Ada … procedure Make_Dir (Dir_Name : String) is C_Dir_Name : String := Dir_Name & ASCII.NUL; function mkdir (Dir_Name : String) return Integer; pragma Import (C, mkdir, "__gnat_mkdir"); begin if mkdir (C_Dir_Name) /= 0 then raise Directory_Error; end if; end Make_Dir; … /* C */ int mkdir (const char *s) { … }
  43. http://libre.adacore.com © AdaCore under the GNU Free Documentation License "C

    Can Call Ada" You can call Ada routines from C code -- Ada … procedure My_Example (X, Y : Integer); pragma Export (C, My_Example, "_ada_example"); … /* C */ extern void _ada_example (const int x, y); void try_it () { _ada_example (3, 4); }
  44. http://libre.adacore.com © AdaCore under the GNU Free Documentation License The

    real cost of programming Programmers do not spend most of their time programming. As projects get larger, less time is spent coding and more is spent in debugging, testing, design, paperwork... Extreme language fluency is not needed if when you just read code C might be better in the activities where programmers spend less time Ada emphasis on readability, safety and maintainability is better suited in the tasks where programmers spend most of their time Steven McConnell
  45. http://libre.adacore.com © AdaCore under the GNU Free Documentation License If

    C is not that great, why there are no more projects in Ada? C fame is based on marketing, tradition and popularity Programming language knowledge is overrated. • A language is just a tool, not a skill There is no need for C programmers • But there is a need for embedded software programmers There is no need for PHP/Javascript programmer • But there is a need a web applications programmers There is no need for Java/Oracle/Hibernate programmer • But there is a need a programmers experienced in object oriented programming, relational databases and object-relational mapping