Programming in the Small: C/C++/Java Pitfalls & Ada Benefits Franco Gasperoni [email protected] http://libre.adacore.com/Software_Matters Suggested Reading C Traps and Pitfalls • by Andrew Koenig (Addison Wesley) Guidelines for the Use of the C Language in Vehicle Based Software • Purchasing info at http://www.misra.org.uk/misra-c.htm Multilanguage Programming on the JVM: The Ada 95 Benefits • http://libre.act-europe.fr/Why_Ada/ada-on-jvm.pdf http://libre.adacore.com © AdaCore under the GNU Free Documentation License 3 Lecture Summary In this lecture we concentrate on programming in the small We go over some Ada basic constructs • Procedures, functions, types, loops, if and case statements exceptions, etc. We show some C/C++/Java pitfalls We show how Ada avoids them http://libre.adacore.com © AdaCore under the GNU Free Documentation License 5 Programming in the Small with C C makes the following assumption: • Trust the programmers, they never make mistakes • Favor program conciseness over its readability But: • Programmers do make mistakes • Programs are written once but read many times The C foundation of C++ & Java leads to fragile software • Software where it is easy to make mistakes • Software that is hard to read • Software that is hard to change http://libre.adacore.com © AdaCore under the GNU Free Documentation License 12 Note on the Ada Development Environment In this course we use GNAT GAP Edition GNAT is widely available • You can download the sources of GNAT • Pre-built GNAT binaries available for: Linux, Solaris, Windows GNAT GPL Edition available at • http://libre.adacore.com http://libre.adacore.com © AdaCore under the GNU Free Documentation License 14 Background on Ada Programming A Simple Example Procedure Main 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; Note: Ada is case insensitive http://libre.adacore.com Stored in file main.adb Technically this is called a compilation unit A compilation unit can be the body or the spec (specification) of a: • • • procedure function package (see next lecture) Spec = precise list of services exported Body = implementation details In GNAT: • • • 1 compilation unit per file File name matches unit name 2 file extensions possible - .adb = Ada Body - .ads = Ada Spec © AdaCore under the GNU Free Documentation License 16 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; http://libre.adacore.com Declarative part: Contains the declaration of: • Variable, types, nested procedures, nested functions, ... used in procedure Main Procedure statements © AdaCore under the GNU Free Documentation License 17 with Text_IO; List of compilation units whose services are used in 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; http://libre.adacore.com Text_IO is the predefined Ada text Input/Output library Procedures declared in library Text_IO and used in Main © AdaCore under the GNU Free Documentation License 18 use Text_IO; with Text_IO; use 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; http://libre.adacore.com By putting a use clause you can (but don't have to) omit the Text_IO prefix inside Main © AdaCore under the GNU Free Documentation License 19 The ' img Attribute with Text_IO; use 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; http://libre.adacore.com ' img is a predefined GNAT attribute Given an integer or floating point number X, X ' img returns its string representation • More on attributes later on • © AdaCore under the GNU Free Documentation License 20 Structure of an Ada Program General Structure of an Ada Program with …; with …; with …; with …; with …; with …; procedure Some_Main is … begin … end Some_Main; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 22 A Simple Example with Text_IO; use Text_IO; procedure Display (S : String; X, Y : Integer) is begin Put_Line (S & " (" & X'img & ") = " & Y'img); end Display; function Fact (N : Integer) return Integer is begin if N <= 1 then return 1; else return N * Fact (N - 1); end if; end Fact; with Display; with Fact; procedure Display_Fact is begin for K in 1 .. 4 loop Display ("Factorial", K, Fact (K)); end loop; end Display_Fact; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 23 Building & Executing Display_Fact GNAT has an automatic "make" facility: gnatmake Gnatmake Will compile or recompile the Ada sources that need to be compiled • It will bind and link them • A make file is not necessary http://libre.adacore.com © AdaCore under the GNU Free Documentation License 24 Note on the Concatenation Operator & with Text_IO; use Text_IO; procedure Display (S : String; X, Y : Integer) is begin Put_Line (S & " (" & X'img & ") = " & Y'img); end Display; 1 2 3 4 5 & A B = 1 2 3 4 5 A B 1 dimensional arrays http://libre.adacore.com © AdaCore under the GNU Free Documentation License 25 Note on For Loops with Display; with Fact; procedure Display_Fact is begin for K in 1 .. 4 loop Display ("Factorial", K, Fact (K)); end loop; end Display_Fact; http://libre.adacore.com In for loops, the loop variable (K in the example) is implicitly declared The loop variable cannot be modified inside the for loop The loop variable ceases to exist just after the loop © AdaCore under the GNU Free Documentation License 26 Numeric Pitfall in C/C++/Java What is the Program Output ? #include <stdio.h> This program compiles fine int main () { int length = 8265; int width = 0252; int height = 8292; What is its output? printf ("length = %d\n", length); printf ("width = %d\n", width); printf ("height = %d\n", height); } http://libre.adacore.com © AdaCore under the GNU Free Documentation License 28 Surprised ? Was this the programmer’s intent ? http://libre.adacore.com © AdaCore under the GNU Free Documentation License 29 Numbers in C/C++/Java In C/C++/Java numbers starting with 0 are octal numbers This is a bad choice • Error-prone • Hard-to-read There is no way to specify numbers in base 2 • Very surprising giving the fact that C was meant for to be a low-level systems language Never use octal numbers http://libre.adacore.com © AdaCore under the GNU Free Documentation License 30 The Ada Version 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; No surprises in Ada http://libre.adacore.com © AdaCore under the GNU Free Documentation License 31 Numbers in Ada Length Width Width_8 : Integer := 8265; : Integer := 0252; -- regular decimal number : Integer := 8#252#; -- octal number B_Mask : Integer := 2#1100_1011#; -- binary number -- you can use “_” to separate digits W_Mask : Integer := 16#FFF1_A4B0#; -- Hexadecimal number If no base is specified the number a decimal number In Ada you can specify any base from 2 to 16 (for both integer and real (floating point) numbers Use the “_” to separate digits for clarity • http://libre.adacore.com 1_000_000_000 © AdaCore under the GNU Free Documentation License 32 Lexical & Syntactic Pitfalls in C/C++/Java Is the Following Code Correct ? #include <limits.h> /* If *y is zero and x > 0 set *k to the biggest positive integer. * If *y is zero and x <=0 leave *k unchanged. * If *y is not zero set *k to be x divided by *y and increment *y by 1. */ void check_divide (int *k, int x, int *y) { if (*y = 0) if (x > 0) *k = INT_MAX; else *k = x / *y /* it is safe to divide by *y since it cannot be 0 */; *y++; } This program compiles fine, but has a number of problems. Which ones? http://libre.adacore.com © AdaCore under the GNU Free Documentation License 34 There are 4 Bugs = versus == • Using “=“ for assignment and “==“ for equality is a poor choice • Use a compiler that warns you when you use “=“ inside tests • This is a hard problem because C++ style encourages the use of “=“ inside tests: while (*s1++ = *s2++); Dangling else problem • Always bracket everything Nested y++ Bad operator precedence *y++ means *(y++) • When in doubt use parentheses or separate tokens with white spaces ... http://libre.adacore.com © AdaCore under the GNU Free Documentation License 35 What about Java? boolean safety_flag; boolean danger_flag; • This problem exists in Java for boolean, … but has been fixed for other data types if (safety_flag = danger_flag) { sound_alarm (); Dangling else problem } • Problem is still there This is OK in Java Bad operator precedence *j++ but is often a bug = versus == means *(j++) • No * operator in Java. This Problem has been solved http://libre.adacore.com © AdaCore under the GNU Free Documentation License 36 The Correct Version #include <limits.h> /* If *y is null and x > 0 set *k to the biggest positive integer. * If *y is null and x <=0 leave *k unchanged. * If *y is non null set *k to be x divided by *y and increment *y by 1. */ void check_divide (int *k, int x, int *y) { if (*y == 0) { if ( x > 0) *k = INT_MAX; } else { *k = x / *y /* it is safe to divide by *y since it cannot be 0 */; (*y)++; /* or *y ++ */ } } http://libre.adacore.com © AdaCore under the GNU Free Documentation License 37 Ada Solves all the Previous Pitfalls -- If Y = 0 and X > 0 set K to the biggest positive integer. -- If Y = 0 and X <= 0 leave K unchanged. -- If Y /= 0 set K to be X divided by Y and increment Y by 1. procedure Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is begin if Y = 0 then if X > 0 then K := Integer’Last; -- K is set to the largest Integer end if; else K := X / Y; -- it is safe to divide by Y since it cannot be 0 Y := Y + 1; end if; end Check_Divide; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 38 Simpler Ada Version: "elsif" "and then" -- If Y = 0 and X > 0 set K to the biggest positive integer. -- If Y = 0 and X <= 0 leave K unchanged. -- If Y /= 0 set K to be X divided by Y and increment Y by 1. 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; -- it is safe to divide by Y since it cannot be 0 Y := Y + 1; end if; end Check_Divide; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 39 Lexical & Syntactic Clarity in Ada = means equality while := means assignment • If you use one instead of the other you get a compiler error No dangling else in Ada • if … then … -- Must be terminated by an end if; end if; • If it isn't then you get a compiler error In Ada comments start with -- and go to the end of the line. No other type of comment No ++ operators in Ada No need for a * operator in Ada http://libre.adacore.com © AdaCore under the GNU Free Documentation License 40 Side Note on Ada Attributes Attributes Ada types, objects, and other entities can have attributes An attribute is a property of the type, object, etc http://libre.adacore.com © AdaCore under the GNU Free Documentation License 45 Example of Scalar Attributes Given T some scalar type (Integer, Float, etc) Given X an object of type T T ' First Smallest value in T T ' Last Largest value in T T ' image (X) String representation of X In GNAT you can use X ' img instead of T ' image (X) http://libre.adacore.com © AdaCore under the GNU Free Documentation License 46 Example of Integer ' Last procedure Check_Divide_And_Increment (K : in out Integer; X : Integer; Y : in out Integer) is begin if Y = 0 then if X > 0 then K := Integer’Last; -- K is set to the largest Integer end if; else K = X / Y; -- it is safe to divide by Y since it cannot be 0 Y := Y + 1; end if; end Checked_Divide; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 47 More C/C++/Java Syntactic Pitfalls Is the Following Code Correct ? // If the signal ahead is clear then increase the speed. void increase_speed_if_safe (int speed, int signal) { if (signal == CLEAR); increase_speed (); } This program compiles fine, but has a problem. Which one? http://libre.adacore.com © AdaCore under the GNU Free Documentation License 49 Be Careful of Spurious Semicolons // If the signal ahead is clear then increase the speed. void increase_speed_if_safe (int speed, int signal) { if (signal == CLEAR); increase_speed (); } http://libre.adacore.com © AdaCore under the GNU Free Documentation License 51 The Ada Version is Always Safe -- 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; If you write if signal = CLEAR then ; • You get a compiler error http://libre.adacore.com © AdaCore under the GNU Free Documentation License 52 More Bad Luck in C/C++/Java: Enumerations and Switch Statements enum Alert_Type {LOW, MEDIUM, HIGH, VERY_HIGH}; // C or C++. Java does not have enumerations, you have to use ints instead 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? http://libre.adacore.com © AdaCore under the GNU Free Documentation License 53 Defects in the Previous Code Don't forget break statements C/C++/Java do 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 http://libre.adacore.com 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); © AdaCore under the GNU Free Documentation License 54 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; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 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 55 Combining Cases procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when HIGH | VERY_HIGH => Sound_Alarm; Alert_Police; end case; end Process_Alert; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 56 Using a Default Clause procedure Process_Alert (Alert : Alert_Type) is begin case Alert is when LOW => Activate_Camera; when MEDIUM => Send_Guard; when others => Sound_Alarm; Alert_Police; end case; end Process_Alert; Ada "when others" is equivalent to C/C++/Java "default", and takes away most of the benefit of the checking for all cases covered http://libre.adacore.com © AdaCore under the GNU Free Documentation License 57 Using a Range 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 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 58 Enumeration Types in Ada type Alert is (LOW, MEDIUM, HIGH, VERY_HIGH); procedure P (B : Integer) is A : Alert; begin Compilation error A := B; Enumerations are true types in Ada In C enums are just integers In C++ enums are implicitly converted to ints (not from ints) // C++ enum Alert {LOW, MEDIUM, HIGH, VERY_HIGH}; int k = LOW; Alert a = 1; http://libre.adacore.com // accepted by C++ // rejected by C++ © AdaCore under the GNU Free Documentation License 59 Ada Enumeration Types and Attributes http://libre.adacore.com Alert_Type ' First LOW Alert_Type ' Last VERY_HIGH © AdaCore under the GNU Free Documentation License 61 Predefined Enumerations in Ada http://libre.adacore.com type Boolean is (False, True); type Character is (…, 'a', 'b', 'c', …); © AdaCore under the GNU Free Documentation License 62 Predefined Enumerations: Examples function Is_Letter (C : Character) return Boolean is begin return (C in 'a' .. 'z') or (C in 'A' .. 'Z'); end Is_Letter; function Is_Arithmetic_Operator (C : Character) return Boolean is begin case C is when '+' | '-' | '*' | '/' => return True; when others => return False; end case; end Is_Arithmetic_Operator; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 63 C/C++/Java Type System What is a Type? A type is characterized by: The set of values an expression of that type can take The operations that can be applied to those values http://libre.adacore.com © AdaCore under the GNU Free Documentation License 71 Pre-Defined and User-Defined Types Some types can be pre-defined by the language • E.g. booleans, integers, characters, strings, etc Pre-defined types come with pre-defined operations • E.g. for integers: additions, subtractions, etc. Languages typically allow user-defined types and operations • User-defined operations are provided in the form of procedures and functions http://libre.adacore.com © AdaCore under the GNU Free Documentation License 72 Typing Problems Common to C/C++/Java typedef in C/C++ is a shorthand it does not define a new type No user-defined types • • • Scalars (characters, integers, reals) Pointers (e.g. there can only be a single pointer to an int type) Arrays (e.g. there can only be a single array of int type) Implicit conversions from integers to reals Weak overflow semantics rules for signed integers Missing types • • • • • http://libre.adacore.com Enumerations in Java (not full types in C/C++) Character types in C/C++ Fixed points Unsigned integers in Java Pointers to functions in Java © AdaCore under the GNU Free Documentation License 78 C/C++ Example 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); } http://libre.adacore.com The program to the left compiles fine There is something wrong with it What ? © AdaCore under the GNU Free Documentation License 80 What's Wrong with C/C++ 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); } http://libre.adacore.com 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/C++ provide NO HELP to the programmer in detecting these mistakes © AdaCore under the GNU Free Documentation License 82 Things are Even Worse in Java There are no typedef in Java Everything must be an int typedef are useful for documentation purposes typedef could be used to perform sanity checks during code walkthroughs or with simple tools This problem is particularly severe in Java given that many API calls have several indistinguishable int parameters: • final int SAFETY_SPEED = 120; … void check_speed (int t, int d) { int s = d/t; if (s < SAFETY_SPEED) increase_speed (t); } void increase_speed (int s) { … } void perform_safety_checks () { int t = get_time (); int d = get_distance (); … check_speed (d, t); } AdjustmentEvent (Adjustable source, int id, int type, int value) http://libre.adacore.com © AdaCore under the GNU Free Documentation License 83 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/C++ or Java to protect the programmer • User defined types • Parameter associations http://libre.adacore.com -- 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; © AdaCore under the GNU Free Documentation License 84 Defining New Types in Ada Users can define their own types in Ada In C/C++/Java users can only define struct/union/class types • No user-defined scalar, pointer or array types -- Example of integer type definition in Ada type Time is range 0 .. 3_600; type Distance is range 0 .. 1_000; type Speed http://libre.adacore.com is range 0 .. 4_000; © AdaCore under the GNU Free Documentation License 85 User Defined Integer Types in Ada Each user defined integer type introduces a new type type Time is range 0 .. 3_600; This new type is NOT a synonym of Integer type Distance is range 0 .. 1_000; type Speed is range 0 .. 4_000; Each user defined integer type gives its bounds, i.e. the values any object of this type can take • • http://libre.adacore.com Time ' First = 0 Time ' Last = 3_600 © AdaCore under the GNU Free Documentation License 86 Ada is Strongly Typed (1 of 2) 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 Compilation S : Speed := D / T; error begin if S < SAFETY_SPEED then Compilation Increase_Speed (T); error end if; end Check_Speed; … http://libre.adacore.com 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 © AdaCore under the GNU Free Documentation License 87 Ada is Strongly Typed (2 of 2) 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 … Compilation Check_Speed (D, T); error end Perform_Safety_Checks; http://libre.adacore.com Parameters switched © AdaCore under the GNU Free Documentation License 88 The Correct Ada Version 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 := Speed ( Integer(D) / Integer (T)); begin if S < SAFETY_SPEED then Increase_Speed (S); end if; end Check_Speed; procedure Perform_Safety_Checks is T : Time := Get_Time; D : Distance := Get_Distance; begin … Check_Speed (T, D); end Perform_Safety_Checks; http://libre.adacore.com You must convert D and T to Integer to perform the division And then convert the result to type Speed © AdaCore under the GNU Free Documentation License 89 But What About? type A_Type is …; How do you know it was procedure Safe_Copy (Source : A_Type; Target : A_Type); procedure Try is X : A_Type := …; Y : A_Type := …; begin Safe_Copy (X, Y); … end Try; http://libre.adacore.com Safe_Copy (X, Y) and not Safe_Copy (Y, X) You don't. That's why Ada provides name parameters © AdaCore under the GNU Free Documentation License 90 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 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 91 Avoiding Parameter Confusion in Ada Summary: Two lines of defense User defined types Named parameters http://libre.adacore.com © AdaCore under the GNU Free Documentation License 92 Example of C/C++/Java Type System Weakness Signed Integer Overflow Semantics Overflow in C/C++/Java #include <limits.h> void compute () { int k = INT_MAX; k = k + 1; } In C/C++ signed integer overflow is undefined, anything can happen • All known implementations "wrap around" In Java wrap around semantics are part of the language http://libre.adacore.com © AdaCore under the GNU Free Documentation License 94 Overflow in Ada procedure Compute is K : Integer := Integer'Last; begin Exception raised K := K + 1; at execution end Compute; time EVERY time there is an integer overflow in Ada an exception is raised http://libre.adacore.com © AdaCore under the GNU Free Documentation License 95 Example: Overflow in Action in Ada In GNAT you have to use the switch -gnato to ask for integer overflow checking http://libre.adacore.com © AdaCore under the GNU Free Documentation License 96 The Pernicious Effects of Wrap-Around Semantics: A Java Example final int RADIO_PORT = …; void open (int port) {…} void send (int port, byte data) {…} void close (int port) {…} void send_bytes (byte first_byte, byte last_byte) { open (RADIO_PORT); for (byte b = first_byte; b <= last_byte; b++) { send (RADIO_PORT, b); } close (RADIO_PORT); } http://libre.adacore.com The program to the left compiles fine, and runs … … But there is something wrong with it. What ? © AdaCore under the GNU Free Documentation License 97 Infinite Loop when last_byte == 127 Two problems: Wrap around semantics of type byte • When last_byte = b = 127 we execute the loop, we do b++ and b wraps to -128 There is no real for loop instruction in C/C++/Java for (x; y; z) {…} • Means x; while (y) { …; z; } http://libre.adacore.com © AdaCore under the GNU Free Documentation License 98 The Ada Version is Safe type Port is range 0 .. 255; type Byte is range -128 .. 127; RADIO_PORT : constant Port := …; procedure Open (P : Port); procedure Send (P : Port; B : Byte); procedure Close (P : Port); procedure Send_Bytes (First : Byte; Last : Byte) is begin Open (RADIO_PORT); for B in First .. Last loop Send (RADIO_PORT, B); end loop; Close (RADIO_PORT); end Send_Bytes; http://libre.adacore.com The code on the left runs fine There is a true for loop in Ada (unlike C/C++/Java) © AdaCore under the GNU Free Documentation License 99 Checks and Overflows Summary In Ada • • • • • Every integer overflow raises an exception in Ada Every division by zero raises an exception in Ada Every array index overflow raises an exception in Ada Etc. You can disable all the Ada checks for deployment if you wish In Java • Java adopted most of the Ada checks except for integer overflow which wraps around in Java • Cannot disable checks in Java In C/C++ • No checks http://libre.adacore.com © AdaCore under the GNU Free Documentation License 100 Side Notes on Ada Types Unsigned Integers Ada has the choice of two sorts of integer types: • Signed integers (an exception is raised in case of an overflow) • Unsigned integers (wrap-around semantics) -- Example of unsigned integers in Ada procedure Try is type Hash_Index is mod 1023; H : Hash_Index := 1022; begin H := H + 1; -- H is equal to zero here end Try; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 102 Subtypes Sometimes you want to add additional constraints to a type without creating a new type Ada provides the notion of subtype for that -- Example of unsigned integers in Ada procedure Try is type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun); subtype Working_Day is Day range Mon .. Fri; D : Day := Mon; WD : Working_Day; begin WD := D; -- This is OK WD := Sun; -- This raises an exception end Try; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 103 Predefined Ada Subtypes subtype Natural is Integer range 0 .. Integer ’ Last; subtype Positive is Natural range 1 .. Natural ’ Last; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 104 Exceptions in Ada When a Check Fails an Exception is Raised in Ada http://libre.adacore.com © AdaCore under the GNU Free Documentation License 106 Ada Predefined Exceptions The following predefined exceptions are raised when something goes wrong in an Ada program Constraint_Error http://libre.adacore.com integer overflow, computation error (divide by zero), array index out of range, null pointer dereferencing, … Storage_Error no more memory available Program_Error fundamental program error (e.g. end of function with no return statement) © AdaCore under the GNU Free Documentation License 107 Creating Your Own Exceptions procedure Checks is Internal_Error : Exception; procedure Foo is begin raise Internal_Error; end Foo; procedure Bar is begin Foo; end Bar; begin -- of Checks Bar; end Checks; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 108 What Happens at Execution Time? procedure Checks is Internal_Error : Exception; procedure Foo is begin raise Internal_Error; 3 end Foo; 2 procedure Bar is begin Foo; end Bar; begin -- of Checks Bar; end Checks; http://libre.adacore.com Exception raised © AdaCore under the GNU Free Documentation License 1 109 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 110 Displaying the Traceback (How you Got There) with Ada.Exceptions; with GNAT.Traceback.Symbolic; with Text_IO; use Ada.Exceptions; use GNAT.Traceback.Symbolic; use Text_IO; procedure Checks is Internal_Error : Exception; procedure Foo is begin raise Internal_Error; end Foo; procedure Bar is begin Foo; end Bar; Exception Handler http://libre.adacore.com begin -- of Checks Bar; exception when E : others => Put_Line ("Raised exception : " & Exception_Name (E)); Put_Line (Symbolic_Traceback (E)); end Checks; © AdaCore under the GNU Free Documentation License 111 -bargs: Program binder arguments: -E: give exception tracebacks -cargs: Compiler arguments: -g: debugging on -gnatl: print out a program listing -gnato: overflow checks on http://libre.adacore.com © AdaCore under the GNU Free Documentation License 112 What Happens at Execution Time with Ada.Exceptions; with GNAT.Traceback.Symbolic; with Text_IO; use Ada.Exceptions; use GNAT.Traceback.Symbolic; use Text_IO; procedure Checks is Internal_Error : Exception; b d http://libre.adacore.com procedure Foo is begin raise Internal_Error; end Foo; a procedure Bar is begin Foo; end Bar; c begin -- of Checks Bar; e exception when E : others => Put_Line ("Raised exception : " & Exception_Name (E)); Put_Line (Symbolic_Traceback (E)); end Checks; © AdaCore under the GNU Free Documentation License 113 Catching a Predefined Exception with Text_IO; use Text_IO; procedure Checks is A : Integer := Integer ’ First; begin A := A - 1; exception when Constraint_Error => Put_Line (“Overflow occurred”); end Checks; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 114 Catching Your Own Exceptions with Text_IO; use Text_IO; procedure Checks is Internal_Error : Exception; procedure Foo is begin raise Internal_Error; end Foo; procedure Bar is begin Foo; end Bar; begin -- of Checks Bar; exception when Internal_Error => Put_Line (“problem occurred”); when others => Put_Line (“some other exception”); end Checks; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 115 Catching an Exception Where You Want procedure Checks is … begin -- of Checks : : : : : : : : : : : : : : : end Checks; http://libre.adacore.com to catch some exception in a region of code without exiting from the subprogram you can use a declare block © AdaCore under the GNU Free Documentation License 116 Example of a Declare Block procedure Calc (A, B : Float) is C, D : Float; begin … declare Old_C : Float := C; begin C := A * B; D := C ** 2; exception when Constraint_Error => C := Old_C; D := 0.0; end; … end Calc; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 117 Array Pitfalls in C Arrays in Ada Ada has real arrays (1-dimensional and multi-dimensional) Ada array can have its size determined at run-time • Local variable length arrays are allowed in the latest C standard (C99) Ada array bounds can be arbitrary, lower bound does not have to start at 0 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 129 One of a Kind Arrays procedure Compute (N : Integer) is In Ada Arrays can have arbitrary bounds A : array (1 .. N) of Float; begin … end Compute; http://libre.adacore.com The bounds can be dynamic values © AdaCore under the GNU Free Documentation License 130 Typed Arrays 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; 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 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 131 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; http://libre.adacore.com Exception raised © AdaCore under the GNU Free Documentation License 132 Example of 1-Dim Array Attributes Given A some array object • A : array (10 .. 99) of Integer; http://libre.adacore.com A ' First 10 A ' Last 99 A ' Length 90 A ' Range 10 .. 99 © AdaCore under the GNU Free Documentation License 133 Ada Arrays are Powerful: No Need to Pass Array Bounds as in C/C++/Java procedure Calc is type Vector is array (Natural range <>) of Float; function Max (V : Vector) return Float is M : Float := Float ’ First; begin for K in V ’ Range loop if V (K) > M then M := V (K); end if; end loop; return M; end Max; V1 : Vector := (1.0, 2.0, 3.0); -- V'First = 0 and V'Last = 2 V2 : Vector (1 .. 100) := (1.0, 2.0, others => 5.0); X : Float := Max (V1); -- X = 3.0 Y : Float := Max (V2); -- Y = 5.0 begin … end Calc; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 134 Ada String Predefined Array Type type String is array (Positive range <>) of Character; R : String (1 .. 10); S : String := (‘H’, ‘e’, ‘l’, ‘l’, ‘o’); T : String := “Hello”; Q : String := S & “ “ & T & “ you”; -- Q = "Hello Hello you" http://libre.adacore.com © AdaCore under the GNU Free Documentation License 135 Records and Pointers in Ada Record Types type Date is record Day : Positive range 1 .. 31; Month : Positive range 1 .. 12; Year : Integer; end record; D : Date := (3, 9, 1975); A : Date := (Day => 31, Month => 12, Year => 1999); B : Date := A; Y : Integer := B . Year; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 137 Pointers and Records type Node; type Node_Ptr is access Node; type Node is record D : Date := (1, 1, 1900); Next : Node_Ptr; end record; P1 : Node_Ptr := new Node; P2 : Node_Ptr := new Node ’ ((3, 9, 1975), P1); 3 9 1975 1 1 1900 null Memory http://libre.adacore.com © AdaCore under the GNU Free Documentation License 138 N : Node := ((31, 12, 1999), null); P3 : Node_Ptr := new Node ’ (N); 31 12 1999 null Memory http://libre.adacore.com © AdaCore under the GNU Free Documentation License 139 Accessing Record Fields: A Simple Rule If P is a pointer to a record then P.all points to the WHOLE record P.Field points to Field in the record Note: P.Field is the same as P.all.Field type Node is record D : Date := (1, 1, 1900); Next : Node_Ptr; end record; P : Node_Ptr := new Node; A_Date : Date := P.D; A_Node : Node_Ptr := P.Next; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 140 Parametrized Records: Discriminants type Q_Array (Positive range <>) of Integer; type Queue (Max_Size : Positive) is record First : Positive := 1; Last : Positive := 1; Size : Natural := 0; Q : Q_Array (1 .. Max_Size); end record; X : Queue (4); -- X.Max_Size = 4 Y : Queue; http://libre.adacore.com Compilation Error Value for Max_Size missing © AdaCore under the GNU Free Documentation License 141 Parameter Passing in C, Java and Ada Three Parameter Passing Modes in Ada in procedure Open_File (X : String); procedure Open_File (X : in String); • It's the default mode, the in can be omitted function Log (X : Float) • Inside the procedure or function X is a constant initialized by the value of the actual parameter return Float; function Log (X : in Float) return Float; • Functions can only have parameters of mode i in out procedure Increment (X : in out Float); • Inside the procedure X is a variable initialized by the value of the actual parameter • The actual parameter is updated with the last value of X when the procedure terminates. out procedure Copy (Y : Float; X : out Float); • Inside the procedure X is an uninitialized variable • The actual parameter is updated with the last value of X when the procedure terminates http://libre.adacore.com © AdaCore under the GNU Free Documentation License 147 Example: Mode "in" function Log (X : Float) return Float is begin X := 1.0; Compilation error … X is a constant inside Log end Log; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 148 Example: Mode "in out" procedure A_Test is Note: In Ada You can nest functions & procedures procedure Increment (X : in out Float) is begin X := X + 1.0; end Increment; Value : Float := 9.0; begin -- Value = 9.0 here Increment (Value); -- Value = 10.0 here end A_Test; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 149 Example: Mode "out" procedure A_Test is procedure Copy (Y : Float; X : out Float) is begin X := Y; end Copy; Value : Float; begin -- Value is uninitialized here Copy (10.0, Value); -- Value = 10.0 here end A_Test; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 150 Ada Offers a Comprehensive Scheme for Safe Low-Level Programming PSW Example System Mask 0 Protection Key unused 7 8 11 12 15 16 Machine State unused 19 20 23 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 http://libre.adacore.com © AdaCore under the GNU Free Documentation License 152 The PSW Type in Ada System Mask 0 7 type State type Byte_Mask type State_Mask Protection Key unused 8 11 12 Machine State unused 15 16 19 20 23 is (A, M, W, P); is array (0 .. 7) of Boolean; is array (State) of Boolean; type Program_Status_Word is record System_Mask : Byte_Mask; Protection_Key : Integer range 0 .. 15; 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; http://libre.adacore.com © AdaCore under the GNU Free Documentation License 153 Accessing PSW Fields in Ada is … Simple … type Program_Status_Word is record System_Mask : Byte_Mask; Protection_Key : Integer range 0 .. 15; Machine_State : State_Mask; end record; … Safe Efficient PSW : Program_Status_Word := …; Key : Integer M_State : Boolean Mask_3 : Boolean http://libre.adacore.com := PSW . Protection_Key; := PSW . Machine_State (M); := PSW . System_Mask (3); © AdaCore under the GNU Free Documentation License 154 There is more in Ada to help with safe and efficient low-level programming than just the previous example http://libre.adacore.com © AdaCore under the GNU Free Documentation License 155 Summary: Better Safe than Sorry Language Safety and Security unsafe assembly http://libre.adacore.com C C++ Java © AdaCore under the GNU Free Documentation License Ada safe 161 Summary A good programming language • Encourages the writing of correct software • Helps in detecting errors C/C++/Java • Encourage the writing of concise code not correct software • C/C++ provide no help in detecting errors • Java provides some help Ada (and other languages such as Eiffel) • Encourage the writing of correct software • Provide help in detecting errors http://libre.adacore.com © AdaCore under the GNU Free Documentation License 162
© Copyright 2026 Paperzz