procedure examples This simple program sums a range of integer numbers. The user is requested to input an integer number “N”, and then the program calculates the sum of the integers from one to N, and stores the value in the variable “sum” Ada “main” procedure, with definitions: with Text_Io; use Text_Io; procedure Myprog is package Int_Io is new Text_Io.Integer_Io(Num=>Integer); use Int_Io; Sum, N: Integer:=0; begin The declarative portion of a procedure is the region between the procedure statement and the keyword “begin”. It is where “local” variables, packages, and nested procedures are declared. Put("please enter an integer value: "); Get(N); New_Line; for I in 1..N loop Sum:= Sum +I; end loop; Put ("the sum of the numbers from 1 to "); Put(N,2); Put(" is: "); Put(Sum); New_Line; end Myprog; The body of a procedure is contained between the keywords “begin” and “end”. Ideally there should be only one exit point from the body of a procedure. Remember: in the for loop in ada the index variable (I in this example) is automatically declared for you, and does not exist after the loop exits. C main function: to sum the numbers from 1 to N #include <stdio.h> int main(void) { int sum=0, n=0; int i; printf(“please enter an integer value: \n”); scanf(“%d”, &n); for ( i = 1; i <=n; i++) { sum = sum + i; } printf(“the sum of the numbers from 1 to %d is: %d\n”, n, sum); } For an example of a simple subprogram, we can move the portion of the program which performs the calculation of sum into a separate subprogram, which I will call sumit. Procedures and functions in ADA In ADA, there are two choices for the implementation of sumit. Sumit can either be implemented as a procedure or a function. Sample Procedure name procedure Sumit(Sum: out Integer; N: in Integer) is begin body sum:=0; for I in 1..N loop Sum:= Sum +I; end loop; end Sumit; formal parameter list Sample function name function Sumit(N: in Integer) return Integer is Sum: Integer:=0; begin for I in 1..N loop Sum:= Sum +I; end loop; return(Sum); end sumit; Result type Formal parameter list Return the result Formal parameters are variables declared in a procedure specification. Actual parameters are variable names, expressions or literal associated with a formal parameter name in a call to a procedure. Formal parameter lists indicate for each formal parameter, the parameter name and type. (in some languages such as ADA, we might also need to indicate how the actual parameter value will be used); parameter ame: sum: mode type; out integer; The mode for ADA is one of in, out or inout: In: The value of the actual parameter is copied into the formal parameter when the procedure is called. The value is transferred into the called procedure and is essentially treated as a constant. The value of the parameter can not be changed and the parameter can only appear on the right hand side of an assignment statement. IN is the only parameter mode allowed in function specifications. out: The value of the formal parameter is copied into the actual parameter after the last statement in the procedure has been executed. The value is transferred out of the called procedure. Out mode parameters can only appear on the left hand side of an expression. in out: The value of the actual parameter is copied into the formal parameter when the procedure is called. After the last statement in the procedure has been executed, the value of the formal parameter is copied back into the actual parameter. So two different data transfers are done with an in out parameter. A value is transferred into the procedure and a modified value is transferred out. If a subprogram needs to return more than one value to the calling program then you should use a procedure. Sample use of sumit: with Text_Io; use Text_Io; procedure Myprog is package Int_Io is new Text_Io.Integer_Io(Num=>Integer); use Int_Io; Sum, N: Integer:=0; procedure Sumit(Sum: out Integer; N: in Integer) is begin Sum:=0; for I in 1..N loop Sum:= Sum +I; end loop; Put("here is sum in sumit "); Put(Sum); end Sumit; begin --begin for procedure myprog Put("please enter an integer value: "); Get(N); New_Line; Sumit(Sum, N); Put ("the sum of the numbers from 1 to "); Put(N,2); Put(" is: "); Put(Sum); New_Line; end Myprog; NOTE, sumit is a “nested” procedure since it is wholly contained inside the declarative portion of procedure myprog. procedure call with actual parameters sum & n. The execution of the procedure body is called an activation: An activation causes an “activation” record to be pushed on the stack: An activation record contains: the “return value” – point in the calling program, and space for the local variable and formal parameters contained in the subprogram. Sample output: please enter an integer value: 5 the sum of the numbers from 1 to 5 is: 15 C implementation #include <stdio.h> int sumit ( int); int main (void) { int sum=0, n=0; Function Prototypes: A function prototype is basically a declaration of a function . It names the function, indicates the return type of the function, and the number and type of formal parameters. In C, we do not need to include the variable name of formal parameters, just their type. We can however if we wish to. This could have been written as: int sumit(int n); printf(“please enter an integer value: \n”); scanf(“%d”, &n); sum = sumit(n); printf(“the sum of the numbers from 1 to %d is: %d, n, sum); } int sumit( int n) { int i; int result=0; for (i=1; i <= n; i++) result = result + i; return(result); } Separation of main programs and the subprograms they call Subprograms can be included in the sample compilation unit (file) as the main program or written/stored in a separately compiled source file. C example In C, function sumit can be stored in a separate file with the extension of .c, and the prototype of function sumit can be placed in a user defined header file. “main.c” #include <stdio.h> #include “sumit.h” int main (void) { int sum=0, n=0; printf(“please enter an integer value: \n”); scanf(“%d”, &n); sum = sumit(n); printf(“the sum of the numbers from 1 to %d is: %d, n, sum); } “sumit.h” int sumit(int); “sumit.c” int sumit( int n) { int i; int result=0; for (i=1; i <= n; i++) result = result + i; return(result); } To compile the program, we would need to include BOTH source files in the gcc command. Also, sumit.h would need to be in the same directory as the files. gcc main.c sumit.c Ada examples In Ada, we have 2 choices for separately compiled subprograms. We can either declare them as “separate” or we can put them into a package. As in C, we can and should declare subprograms. (We will see later when discussing scope rules, a variable/subprogram is not visible/known until it is declared.) A subprogram declaration has the format: procedure name (formal parameter list); function name (formal parameter list) return type; for example: using the previous example of sumit: procedure Sumit(Sum: out Integer; N: in Integer); In the previous example, we could declare function sumit as “separate” . Sumit is still a child procedure of procedure myprog (logically nested inside it), but this allow us to take the body of sumit, and place it in a physically separate source file. There are advantages to doing this: In improves the readability of procedure myprog. This is especially important if nested subprograms are very long. Example File: myprog.adb with Text_Io; use Text_Io; procedure Myprog is package Int_Io is new Text_Io.Integer_Io(Num=>Integer); use Int_Io; Sum, N: Integer:=0; procedure Sumit(Sum: out Integer; N: in Integer) is separate; begin --begin for procedure myprog Put("please enter an integer value: "); Get(N); New_Line; Sumit(Sum, N); Put ("the sum of the numbers from 1 to "); Put(N,2); Put(" is: "); Put(Sum); New_Line; end Myprog; file: sumit.adb This names the parent procedure. separate (myprog) procedure Sumit(Sum: out Integer; N: in Integer) is begin Sum:=0; for I in 1..N loop Sum:= Sum +I; end loop; Put("here is sum in sumit "); Put(Sum); end Sumit; To compile the program I could use: ada95 myprog.adb sumit.adb Packages Alternately, subprograms can be separated into packages. A package is similar to the header file/source file construct in C. A package is composed of two separate files. A “specification” file (with the extension .ads) which contains the definitions of user defined data types, variables, constants, and subprograms that the package will make available to the user. The package “body” contains the implementation of subprograms declared in the specification. Optionally the package body may contain the declaration of additional types/variables which are ONLY available to the subprograms contained in the package. Fraction Package To illustrate packages, we will use a slightly more complex example which implements a “fraction” data type, and provides some basic arithmetic operations on variables of those types. The “ads” file is equivalent to a “header” file in C. It contains type declarations, and function/procedures specifications (prototypes). The “adb” file contains the actual implementation of the bodies of the functions/procedures declared in the ads file. Fraction_package.ads package Fraction_Package is type Fraction_Type is private; The implementation of private types are hidden from the users of the package. The only way users of this package can manipulate fractions is via the operations provided by the package. function Add( A: in Fraction_Type; B: Fraction_Type) return Fraction_Type; procedure Set(A: out Fraction_Type; Numerator: in Integer; Denominator: in Integer); procedure Print(A: in Fraction_Type); private type Fraction_type is record Numerator: Integer :=0; Denominator : Integer :=0; end record; end Fraction_Package; Actual declaration of type Fraction_type. Fraction_package.adb with Text_Io; use Text_Io; with Ada.Integer_text_Io; use Ada.Integer_text_Io; package body Fraction_Package is function Add(A: in Fraction_Type; B: in Fraction_Type) return Fraction_Type is Result : Fraction_Type; GCD : Integer; begin if (A.Denominator > B.Denominator) then Gcd := A.Denominator; else Gcd := B.Denominator; end if; Result.Numerator := ((A.Numerator * B.Denominator) + (B.Numerator * A.Denominator))/gcd; Result.Denominator := (A.Denominator * B.Denominator)/gcd; return Result; end Add; procedure Set(A: out Fraction_Type; Numerator: in Integer; Denominator: in Integer) is begin A.Numerator := Numerator; A.Denominator := Denominator; end Set; procedure Print(A: in Fraction_Type) is begin Put("the fraction is: "); Put(A.Numerator,3); Put(" /"); Put(A.Denominator,3); New_Line; end Print; end Fraction_Package; Sample of using package fraction_package with Text_Io; use Text_Io; with Ada.Integer_text_Io; use Ada.Integer_text_Io; with fraction_package; use fraction package procedure use_fractions is A, B, C: fraction_type; Begin Put(“WE will initialize two fractions, A & B, add them and store the result in C”); Set (a, 1, 2); Set (b, 1, 4); --set A to one-half --set B to one-fourth C := add (A, B); Put(“the value of C is: ” ); Print(C); End use_fractions; Scope rules examples Essentially, in any language a variable/subprogram is “known” or accessible only from the point in which it is declared onward until the end of the construct in which it was declared. Variables declared in the declarative portion of a subprogram or inside a block ({} in C, and Begin end in Ada) are LOCAL to that block or procedure. Which means they can only be accessed inside that procedure/block and any procedures nested inside the parent procedure. Global variables are declared outside the body of any procedure and are accessible to ALL subprograms contained inside the compilation unit. use Text_Io; with Ada.Integer_Text_Io; use Ada.Integer_Text_Io; procedure Scope is X: Integer :=89; Y: Integer:=3; procedure Ado(Z: in out Integer) is X: integer; begin X:= 2341; Y:= 654; end Ado; begin Put("X in the main program is: "); Put(X); New_Line; Put("Y in the main program is: "); Put(y); Variables declared inside a procedure are local to that procedure and any subprograms nested inside the BODY of the procedure. So X and Y are visible to both procedure SCOPE and procedure ADO. However the formal parameter X in Ado, supercedes the declaration of X in Scope. If a local variable is declared which has the same name as a global variable, or variable in the parent procedure. The local declaration supersedes the previously declared variable. In this instance the local copy of X in Ado is the one changed to 2341. Typically the scope of a variable for nested procedures is from the point which it is declared until the end of the construct in which it was declared New_Line; Ado(X); Put("X after the call to ado is: "); Put(X); New_Line; Put("Y after the call to ado is: "); Put(y); New_Line; end Scope; The programs output is: X in the main program is: Y in the main program is: X after the call to ado is: Y after the call to ado is: 89 3 89 654 C Scope Example In C since we do not have the issue of nested procedures, all variables are either local to a specific function or global #include <stdio.h> int x = 89; int y = 3; int main (void) { printf(“X in the main program is: %d\n”, x); printf(“Y in the main program is: %d\n”, y); ado(x); printf(“X after the call to ado is: %d\n”, x); printf(“Y after the call to ado is: %d\n”, y); return; } int ado(int z) { int x; x = 2341; y = 654; return } C pointer example: #include <stdio.h> int change(int *x); int changevalue(int *y); int main(void) { int *a, *b; b = (int *) malloc(sizeof(int)); *b = 5; printf("here is b before change: %d\n", *b); change(b); printf("here is b after change: %d\n", *b); changevalue(b); printf("here is b after changevalue: %d\n", *b); return 0; }/*main*/ int change(int *x) { x = (int *) malloc(sizeof(int)); *x = 12; return 0; }/*end change*/ int changevalue(int *y) { *y = 234; return 0; } /* end changevalue*/ The output of running this example is: here is b before change: 5 Here is b after change: 5 here is b after changevalue: 234