Process 60-141 Lecture 1: Functions C Library predefined, User defined functions Dr. Robert Kent Review 60-140 (and 60-100 or 62-140) Lecture 1: Outline The function concept C Library Functions User Defined Functions Storage Classes and Scope Rules Summary The function concept F(x,y) Process The word function has different grammatical usages, all of which convey the notion of action, or performing something useful The word is embedded in common (natural) language The mosquito has a function in the ecosystem. This car is functioning well. The square root function applied to a number X obtains a number Y which, when multiplied by itself, reobtains X. What are the functions of an operating system? The function concept F(x,y) Process From mathematics, we develop an appreciation of functions as formal objects, having Names (descriptives) cube logarithm integral Well-defined by strict algebraic properties and algorithms, including limitations on applicability square root Eg. The cube function applied to X is found by multiplying X by itself three times. Eg. The set of all real square roots is determined over application of the square root function to the non-negative real numbers only. Associated values whenever specific numeric values are applied Eg. Applying the square root function to 4 returns the value 2. The function concept F(x,y) Process In computer science, programming languages have evolved to incorporate the concept of functions As in mathematics, programming functions have Names Well defined logic that determines the actions to be performed Limitations on applicability Associated values when specific input parameters are applied float Radius_of_circle_squared ( float X, float Y ) { return X*X + Y*Y ; } F(x,y) = R 2 = F(2,3) = x2 + y 2 2 2 + 3 2 = 13 for all (x,y) real The function concept Re-usable expertise .... Standing on the shoulders of giants .... But we may discover new functions that must be defined and named by user programmers Process Again, following mathematics, we inherit some functions with predefined names and definitions F(x,y) These also comprise the body of developing expertise In this lecture we explore many facets of both predefined functions from the C library system, and also user defined functions that must be defined. C Library Functions Predefined Functions Predefined process C Library Functions C libraries C Compiler and Pre-processor Operating Systems, Loaders and Linkers Standard Input-Output Library <stdio.h> Math Library <math.h> Standard Utilities Library <stdlib.h> C libraries The C library system is designed to support special purpose programming needs, depending on the conceptual nature of the need. The system is subdivided into specific libraries, each devoted to a particular topic (set of functions) Each has its own name (eg. stdio.h) Programs that refer to any function in a library must include all the functions in that library. This strategy is based on the premise that most programming work that requires one function, most likely will also use the other functions Since all function codes must be included in the fully compiled program, it is best to avoid unnecessary codes from other (nonincluded) libraries C Compiler and Pre-processor It is important to understand the role of the C compiler and pre-processor in how functions are handled We have already seen in basic programs that in order to perform simple input (scanf) and output (printf) we must write the line of code: #include <stdio.h> This code provides a stated link to a file (called a header file - .h) that the compiler must know in order to properly translate references to functions defined within the library stdio.h contains the definitions of some parts (stubs) of the functions defined within the standard input/output library The actual executable files (already compiled) corresponding to each function (or the entire library) are known to the O/S. C Compiler and Pre-processor #include <stdio.h> int main ( ) { int V = 7 ; ..... printf ( “Value = %d\n”, V ) ; ..... return 0 ; } File: stdio.h ..... int printf ( char *S, void *V ) ; ..... C program with function reference from standard I/O library The actual machine code is filed elsewhere for location by the O/S Compiled file for stdiolibrary 010000111101010101.... (printf machine code) 01110101010111111010.... ....... (scanf machine code) 1010101010111101011..... The C preprocessor looks for function definitions within stdio.h file C Compiler and Pre-processor C program with function #include <stdio.h> reference from standard int main ( ) { I/O library int V = 7 ; The actual machine code ..... Compiled file a.out is filed elsewhere for printf ( “Value HEADER = %d\n”, CODE: V); location by the O/S ..... Resources required, including Compiled file for stdiolibrary 010000111101010101.... return 0 ; machine codes for I/O. (printf machine } PROGRAM MACHINE CODE: code) 01110101010111111010.... 11010010001011010..... ....... .... (scanf machine code) File: stdio.h 00010101001010101 1010101010111101011..... ..... The compiled program, a.out, contains translated machine codes for int printf to ( char *S, invoid *V ) ;but the actual function machine references functions the library, The C preprocessor looks ..... are referenced in a part of a.out called codes the file header. They are for function definitions not integrated with a.out until the program is loaded for execution. within stdio.h file Operating Systems, Loaders and Linkers Operating Systems programs are responsible for managing the computer’s resources and how they are allocated, scheduled and used by user-programs The request to load a program (a.out) for execution is done by entering the executable filename at the system prompt %a.out The O/S responds to this user request by looking at the file a.out in the user’s filesystem (what you are looking at when you log on) and verifying that it is an executable program Otherwise, a system error message is outputted Operating Systems, Loaders and Linkers The O/S contains many programs that handle specific tasks: The Resource Allocator reads and interprets the header part of the a.out file to determine which resources will be needed and how much RAM to allocate to the program The Loader is responsible for finding all parts of the machine code files needed to complete the full user-program and place those files into RAM locations allocated for this purpose The Linker is responsible for resolving all machine RAM addresses between the different files Operating Systems, Loaders and Linkers Res Alloc Loader Compiler User C program a.out Final loaded, resolved executable process Linker C library system stdio.h stdio.h math.h math.h stdlib.h stdlib.h DLL library system Three Libraries We will now turn our discussion to three of the C libraries C libraries: Standard Input/Output Library - stdio.h Math Library – math.h Standard Library – stdlib.h Standard Input-Output Library <stdio.h> As the name suggests, the Standard Input-Output Library header file <stdio.h> contains definitions (stubs) of several useful, predefined functions to perform input and output of characters and constants such as EOF Standard Input-Output Library <stdio.h> The functions can be divided into categories based on whether I/O involves a single character I/O involves a string of characters that are not converted I/O involves a string of character data that must be converted from character to internal machine representation (or vice versa) Memory based conversions involving a string of character data that must be converted from character to internal machine representation (or vice versa) Standard Input-Output Library <stdio.h> Single character I/O is provided with two functions putchar getchar char Ch ; .... Ch = ‘W’ ; .... putchar( Ch ) ; /* can output any valid character, including control */ char Ch ; .... Ch = getchar( ) ; /* can input any valid character, including control */ Both of these functions are used to do I/O with only one character, but this is the beginning of string processing where groups of characters are manipulated. Standard Input-Output Library <stdio.h> <stdio.h> provides definition of a special constant, called the end-of-file marker, EOF. /* Alternatively: */ The actual value of EOF may vary on different systems, = getchar() but isChtypically the ;value -1. while ( Ch != EOF ) { ..... The getchar() and scanf() functions return a value which Ch = getchar() ; can be } assigned to a variable (int or char) /* Once thecan input obtained, even EOF This variable thenisbe compared withthe EOF to “detect” the value, itcondition can be stored and referenced later */ end-of-file while ( ( Ch = getchar() ) != EOF ) { ..... } Standard Input-Output Library <stdio.h> There are also two functions that are used to perform I/O on strings of characters A string in C is an array collection of char values, delimited by a special character ‘\0’ (backslash-zero) Literal strings are expressed as a sequence of valid characters, including controls, contained within quotation marks puts The notion of a string will be discussed thoroughly in 60-141 (Chapters 6, 8) ..... puts ( “This is an output string 1 2 3 \n” ) ; gets To be discussed at a later time. Standard Input-Output Library <stdio.h> Previously, we introduced and discussed the printf and scanf functions and mention them here again as a reminder printf ( FormatString [, opt. Parms or Exprs ] ) ; .... printf ( “I am a string\n” ) ; .... printf ( “Value = %d”, intVal ) ; .... printf ( “Value = %f”, 0.5 * (X+Y) ) ; scanf ( FormatString [, opt. DestParms using & ] ) ; .... scanf ( “%d%f”, &intVal, &floatVal ) ; Standard Input-Output Library <stdio.h> There are two functions provided that handle strings of characters and are very similar to printf/scanf, but which do not actually perform I/O. sprintf sscanf These functions process string and numeric data but all operations take place in RAM. We will not discuss these at this time Math Library <math.h> As the name suggests, the Math Library header file <math.h> contains definitions (stubs) for several often used mathematical functions Some of these are straightforward but used often enough to warrant inclusion to save programmer time These are divided into several categories, including basic math, trigonometric and exponentiation/logarithm functions Math Library <math.h> The math functions are all based on well-known operations, but it is useful to review them in the programming context Math Library <math.h> sqrt pow fabs ceil fmod sin exp floor cos log tan atan log10 Math Library <math.h> Math Library <math.h> Elmo has a 10 meter long ladder. He needs to lean it against the house sqrt so that the top of the ladder is exactly 6 meters above the ground. double X, Y ; .... Y = sqrt ( X ) ; /* X >= 0 */ How far away from the house must Elmo set the base of the ladder? Pythagorus Theorem: Distance = sqrt ( 10 * 10= -Base*Base+Height*Height 6 * 6 ) = sqrt ( 64 ) = 8.0 pow Hypotenus*Hypotenus double X, Y, Z ; .... Z = pow ( X, Y ) ; /* X to power Y */ /* Use with care ! Must be computable. */ 6m 10 m fabs double X, Y ; ? Y = fabs ( X ) ; /* absolute value */ .... Math Library <math.h> Math Library <math.h> Consider the real division of X by Y : ceil ceil(x) = x floor From this we denote: X realRemainder int N ; float In .... N = ceil ( Xalgebraic notation ------ X = ;mathematical, intQuotient + ) ;--------------------/* ceil( 4.0) = 4 these ceil( 4.001 ) = 5 */ are represented Y Y as : floor(x) = x int N ; float X ; .... N = floor ( X ) ; /* floor( fmod 4.0) = (4 X, floor( ) = 4 */ Y ) =4.9999 realRemainder fmod (the “real” remainder) float X, Y, Z ; .... Z = fmod ( X, Y ) ; /* divide 4.5 by 2.1 to get fmod( 4.5, 2.1) = 0.3 (out of 2.1) */ Math x exp ( x ) = e log ( x ) = Library <math.h> ln ( x ) C x Math Library <math.h> Math A ln ( e ) == x Identity ! a Comp logcos ( exp ) ) != atan x Trigonometry : sin B ( xtan double X, Y,sin RadianAngle Equality not guaranteed due to limitations of (a) = A / C cos(a) =B/C ..... X = sin ( Y )machine ; .... Yprecision. = cos ( RadianAngle ) ; .... RadianAngle = atan ( X / Y ) ; .... tan(a) = A / B atan(A/B) = a pow ( 10, x ) = 10 x log10 ( x ) = log10 (x) Exponential/Logarithm : exp log log10 double X, Y ; .... Y = exp ( X ) ; ..... X = log ( Y ) ; /* natural logarithm */ .... X = log10 ( Y ) ; /* base-10 logarithm */ Math Library <math.h> It is good practice to try tomust develop some of the Since any calculation be completed in a function finite algorithms willit is encounter time you period necessary to truncate the series a certain number of terms Consider theafter trigonometric function sin(x)T(K). How to calculate it? This implies that any numeric calculation is only an Use Taylor’s Theorem on continuous functions approximation and not an exact algorithm. sin(x) = x - x3 / 3! + x5 / 5! - ... + (-1) K x2K+1 / (2K+1)! ... = T(0) + T(1) + T(2) ...... + T(K) .... This is called iteration, where we add successive terms to an accumulator. The value in the accumulator must converge to a specific value to be well defined. Math Library <math.h> In general, the (K+1)’th term is expressed T(K+1) = (-1) K+1 x2K+3 / (2K+3)! Note that this requires (2K+3) multiplications of x and evaluation of (2K+3) factorial (recall how factorial overflows for small values of K). We use a “trick” (a useful strategy) T(K+1) = (-1) K+1 x2K+3 / (2K+3)! = - (-1) K x2K+1 / (2K+1)! x2 / ((2K+3)(2K+2)) = - T(K) * x2 / ((2K+3)(2K+2)) This needs only 4 multiplies, 1 division, and no overflow typically Now one can develop a loop that computes each successive term and adds it to the sin accumulator until it converges to a satisfactory limit. Math Library <math.h> The following codes illustrate such an algorithm #include <math.h> ½ * (Snew + Sold ) is the average float X, Sold, Snew, T, Slim = 0.00001 ; (Snew-Sold) is the difference Snew = T = X ; We are demanding that the ratio of the difference do and{ the average be less than the lower limit, Slim Sold = Snew ; T = - T * X * X / (K+K+3) / (K+K+2) ; Snew = Snew + T ; } while( 2.0*fabs(Snew-Sold)/(Snew+Sold) >= Slim); printf ( “sin(%f) = %f \n”, X, Snew ) ; Standard Utilities Library <stdlib.h> The Standard Utilities Library <stdlib.h> contains definitions for a number of general purpose functions Some of these will be discussed, such as those below that are used to convert data from one machine representation to another, or request memory allocation atoi atof atol strtod strtol strtoul malloc calloc We will discuss one very useful function involving the concept and use of pseudo-random numbers Standard Utilities Library <stdlib.h> Randomness is a property of nature that refers to the unpredictability of events Random numbers are any sets of numbers whose pattern cannot be predicted Radioactive decay of fissile materials cannot be predicted Unfortunately, computer algorithms are deterministic, hence they cannot (in principle) produce randomness Pseudo-random numbers refer to a set of numbers whose apparent behaviour resembles a set of truly random numbers Values are distributed evenly within an interval [A..B] Order of production “appears” to be random, even though it is not random Standard Utilities Library <stdlib.h> Pre-defined pseudo-random functions rand int N ; .... N = rand ( ) ; Produces an integer value in the range from 0 to RAND_MAX (defined within stdlib.h) Each time rand() is called (used) it outputs a different value But, it must be noted that the values repeat themselves if rand() is called a large number of times Values generated are evenly distributed within the range Standard Utilities Library <stdlib.h> Pre-defined pseudo-random functions rand int N ; N = rand ( ) ; This can be adapted to produce values within a range [0..L) by scaling with L using the modulus .... N = rand() % L ; Unfortunately, rand() always produces the same sequence of values, starting at the same value. Eventually the sequence repeats itself. Standard Utilities Library <stdlib.h> Example: Rolling the dice – is it a gamble? Example Code: int playgame, T1, T2 ; do { printf ( “Quit (0) or Roll again (1) > “ ) ; scanf ( “%d”, &playgame ) ; if ( playgame ) { T1 = rand() % 6 + 1 ; T2 = rand() % 6 + 1 ; printf ( “You threw a %d and a %d\n”, T1, T2 ); } } while ( playgame ) ; Standard Utilities Library <stdlib.h> We can partially overcome the limitations of rand() by seeding the pseudo-random number generator algorithm used in C. srand unsigned int Seed ; .... scanf ( “%d”, &Seed ) ; srand ( Seed ) ; If a value of Seed is supplied (say, by inputting it) and it is chosen “randomly”, then the pseudo-random sequence generated thereafter by rand() begins at a different point But the sequence is still not random Standard Utilities Library <stdlib.h> There is another library, <time.h>, which is useful for seeding the pseudo-random sequence using the current time (supplied by the system) #include <time.h> #include <stdlib.h> srand ( time( NULL) ) ; When used in programming, this avoids the need to enter a seed value and the program uses a different time each time it is run. Other Libraries Many other libraries do exist in the C programming C99 : system. There is aerrno.h newer dialect C language called assert.h float.h of the limits.h C99. stdarg.h signal.h time.h stddef.h setjmp.h locale.h This version provides additional libraries, such as complex numbers and functions and some networking and communications Some of these libraries will be discussedfunctions. in 60-141 Especially the string handling library – string.h and the character handling library – ctype.h We now turn attention to user-defined functions .... where the student learns to write their own functions User Defined Functions Modular programming User Defined Functions Modular design and programming Basic function template Function prototypes and definitions Functions and stacks Call by value Introduction to pointers Call by (address) reference Recursion Modular design and programming When we began the course, we discussed the difficulties associated with designing large, complex programs Modular design refers to the breakdown of large problems into smaller units, or modules, each of which is easier to solve Design approaches: Top-Down Bottom-Up Stepwise refinement Workflow Structured programming Identification of reusable modules C provides capabilities for user programmers to define their own modules in the form of functions. Basic function template Each function defined in a C program follows a basic function template (structure) functype FuncName ( [opt. parmtype ParmName] ) { /* data declarations */ /* function body – executable statements */ return [functype value] ; } Basic function template Examples: /* Assume int A,B; float U,V; */ int isquare ( int X ) { return X * X ; } /* Usage: B = isquare(A) ; */ FuncType FuncName ParmType ParmName ReturnVal float fsquare ( float X ) { return X * X ; } /* Usage: U = fsquare(V) ; */ Basic function template Examples: double mysin ( double X ) { double T = 0.0, Sold, Snew, Slim = 0.00001 ; int K = 0 ; Snew = T = X ; do { Sold = Snew ; T = - T * X * X / (K+K+3) / (K+K+2) ; K++ ; Snew = Snew + T ; } while( 2.0*fabs(Snew-Sold)/(Snew+Sold) >= Slim); return Snew ; } Function Prototypes and Definitions Most design work begins using a Top-Down approach. During this phase one is not concerned with details, necessarily, rather high-level concepts. It is usually straightforward to identify need for functions that will accomplish a task, together with the input data and the required output (if any). Typically, the designer is concerned with the general function concept, but not with the specific algorithm that defines the function. This leads to the problem that in C programming it is required that all symbolic references, including function references, demand prior definition of the function. Otherwise, compilers cannot correct resolve references Function Prototypes and Definitions To support rapid application development (RAD), programmers usually define functions in two stages A prototype of the function is provided before the main function definition. The form of the prototype statement permits the compiler to translate function references. The actual full definition of the function is then placed after main and at a later stage of program development. Function Prototypes and Definitions Prototypes Formal statements that describe the structural attributes of a function. Under some circumstances a prototype is not required and a full function definition is supplied. Definitions Formal, rigorous definitions of the structure and the internal logic of a function. The definition may be placed before main – in such cases a prototype statement may be eliminated as unnecessary. #include <stdio.h> In prototype statements, it Function PrototypesNOTE: and Definitions is not necessary to provide double mysin ( double ) ; names of parameter variables – only their data types are required. int main ( ) { double Angle = 1.57 ; printf ( “The sine of %lf = %lf\n”, Angle, mysin( Angle ) ) ; return 0 ; } HOWEVER: In function definitions it is required that double mysin ( double X ) { all parameter names be double T = 0.0, Sold, Snew, Slim = 0.00001 ; since they will be specified, int K = 0 ; Snew = T = X ; referenced within the function do { body. Sold = Snew ; T = - T * X * X / (K+K+3) / (K+K+2) ; K++ ; Snew = Snew + T ; } while( 2.0*fabs(Snew-Sold)/(Snew+Sold) >= Slim); return Snew ; } Operating System Functions and stacks When programs are compiled, the file created (a.out) is composed as a set of machine coded modules, each Code corresponding to a particular need A module for executable instructions (code) A module for certain kinds of data (data) (Static) Data In general, programs require additional modular Heap structures within RAM, when loaded for execution Buffer modules for I/O Heap space for dynamic memory allocations I/Orequested Buffers by the program during execution Stack space for storing and retrieving function data dynamically during execution Stack Functions and stacks The role of the stack is an important detail in understanding how functions operate during execution Each function has an associated data structure that groups all the function variables, including the function value itself (the return value), plus additional information, into a single data module (stack data frame, also called an activation record). Every time a function is called, an allocation of stack memory is made – just enough for the data frame. Functions and stacks The life-cycle of a function call is detailed below Stack space is allocated for the function frame The frame is initialized with data supplied Within the of function definition course, students will learn As part the 60-141 about Through calling interface using the values supplied a the subject (and techniques) called dynamic The address of the return point (where the function was called from) memory allocation. As These the function code is executed, all variable techniques will teach how to references constructare a made to the frame locations assigned (by the compiler and linker) stack and function frame data structures, as well The final result is stored in the frame as how to process the data stored in the frame. The return statement obtains the return point address from the frame, then returns to that instruction The stack de-allocates the frame space and re-uses it for other functions There are more details, and intricacies, to follow later. Function Interface – data passing The function interface refers to the design and mechanisms implemented for passing data into and out from a function based on how the function is referenced (called) Input Output (a,b,c) F(a,b,c) F We will discuss two methods of passing data Call by Value (copy) Call by Reference (address) We will also need to introduce the concept of address pointers and operators Y 4 A 4 Call by value Consider the function definition: and the calling statement: int cube ( int A ) { return A * A * A ; } X = cube ( Y ) ; frame When the cube function is called, its function data frame is allocated RAM space on the stack The function variable A refers to an int storage at a stack specific RAM address. The variable Y refers to a different RAM address where an int value has been stored. After the function data frame allocation is completed, the frame is initialized by fetching the integer value stored at Y and copying it to the location A in the frame. Call by value The Call by Value approach is sometimes described as a direct approach because of the way that a copy of original data (whose location is provided directly in the calling statement interface) is created and stored at a well-defined stack location. The technique also provides for process and data isolation. Isolation refers to the fact that any changes made to variables within a function are not reflected in other (external to function) variables. Clearly, there can be a severe efficiency problem if the amount of data passed in to a function is too large It may take more time to move data than to process it and return a result It is best to keep the CPU busy and not the data bus ! Introduction to pointers The Call by Value method permits return of a value using storage allocated on a stack – this storage is limited by the data type attributed to the function (functype). What if we want to pass many values into a function, but also obtain many values back from a function as the results of processing. Matrix inversion or transposition Sort all the values in a list and return the sorted list Find all values in a list and return all their locations in the list We can solve this problem by providing the actual RAM address locations (where data is stored in the calling routine) where we want data taken from, or placed as final results. Introduction to pointers The technique of treating a Ram address as data and passing the address data to a function is called Call by Reference (or Call by Address). We defer further discussion until after we have discussed the concept of pointer in C. We begin by introducing two operators Address_Of :: & Dereferencing :: * Introduction to pointers Address_Of :: & (ampersand) Assume: int W = 0, N = 5 ; int * addrN ; Now the expression addrN = &N ; causes the RAM address NOTE: of the variable N (where the value 5 is stored, but not the value to declare a pointer variable capable itself)Intoorder be stored (assigned) in the variable addrN.of storing the address of a variable (symbolic reference) it is necessary to supply the data Dereferencing * (asterisk) type of the::variable and then the * operator to The expression Wthe = * variable addrN ; being causes the value indicate that defined is anstored at the address stored in addrN pointer (ie. the address address variable.of variable N) to be copied (assigned) to the variable W. Since addrN is a reference to variable N, it is called a pointer (more specifically an int pointer) to N. The expression * addrN dereferences addrN by fetching the value 5 stored at N. This is called indirect addressing. Introduction to pointers It is very important to appreciate the relationship between how a pointer is declared and how it may be used. C is called a strongly typed language because it enforces type compatibility, even using pointers. It does so by ensuring that the data used by dereferencing matches the typing requirements of logical expressions. For instance, there is a difference between a pointer variable of type int* as opposed to another pointer variable of type double*. Call by (address) Reference Assume the declaration ...... and the function definition int X = 4 ; void cube ( int * N ) { *N = *N * *N * *N ; return ; } /* be careful with asterisks !! */ Now consider the calling statement cube ( &X ) ; Note that we pass into the function the Address_Of variable X (ie. &X). Inside the cube function, the variable N contains &X. In order to calculate the cube of 4 (the value of X) it is necessary to dereference N (ie. *N). The final result is assigned indirectly back in the variable X, again using *N to refer to the storage location of the variable X. Call by (address) Reference Assume the declaration ... and the function definition int X = 4 ; void cube ( int * N ) { *N = *N * *N * *N ; return ; } /* be careful with asterisks !! */ When a dereferencing operator is applied, as in *N, the data stored in N (the address of X) is fetched to the CPU. Then the data stored at this stored address is used to fetch the required data at the location X (ie. the value 4). Thus, two memory references (fetches) are made in order to acquire the needed data for calculations. As we shall see later, the main power of Call by Reference using pointers lies in how it supports access to large sets of data. Recursion Recursion is a strategy for solving problems in one (large) domain by using solutions based on the same strategy obtained from application to smaller domains. Consider the factorial problem, F(N) = N ! Limitations: Base case 0 : Base case 1 : Recursive case: F(N) not defined for N < 0 0! = 1 1! = 1 F(N) = N * F(N-1) C supports programming where a function calls itself. This is called recursion in programming. Recursion Consider the factorial problem, F(N) = N ! Limitations: Base case 0 : Base case 1 : Recursive case: F(N) not defined for N < 0 0! = 1 1! = 1 F(N) = N * F(N-1) int Factorial ( int N ) { if ( N < 0 ) return -1 ; /* indicate negative input */ if ( N == 0 || N == 1 ) return 1 ; /* base cases */ return N * Factorial ( N-1 ) ; /* recursive step */ } Recursion Recursion is an alternative to Iteration. Both involve repetition Recursion repeatedly calls a function and therefore uses the call stack to store data – this takes time Recursion is based on selection Must be able to terminate the recursive algorithm by selecting a base case However, iteration involves a control structure that must still make a decision to loop again, or exit the loop. Recursion is elegant Iteration has higher performance, typically Both have advantages and disadvantages – learn both !! Storage Classes & Scope Rules The fine art of referencing ... 3C : Storage Classes & Scope Rules Symbolic Referencing Issues Storage Classes Scope Rules Symbolic Referencing Issues We use symbolic names as a vital part of C programming in order to declare names of variables, functions and other items. We have already encountered one important rule concerning referencing of symbol names – we must declare all names before they may be referenced. It is necessary to understand all the rules concerning how we may reference symbol names And whether we may even be prohibited from referencing names under certain conditions Storage Classes In C programs, symbolic names (identifiers) that refer to data storage (variables, functions) fall into two categories of storage class depending on their duration (lifetime) Automatic storage duration (short lifetime) Some identifiers exist in memory for only a brief period Others exist for the entire time the program itself exists in RAM auto register Static storage duration (long lifetime) static extern Storage Classes Automatic storage duration is used for identifiers that exist only briefly. There are two subclasses Auto storage is the default storage class. It refers to all variables that are defined inside of any function (unless explicitly defined otherwise) auto int N ; is the same as int N ; Auto variables are allocated space in RAM when functions are called (ie. space on the stack). These storage spaces are de-allocated when leaving the function. Storage Classes Register storage is a special class and NOTE: refers to data that Not every system and C compiler will is stored in CPU hardware registers support the register storage class option In this example, N does not refer to a RAM location, rather a CPU register location. Data is moved to the register and subsequently manipulated at that location register int N ; – in such systems the register declaration defaults to auto storage class. This avoids the need to move data back and forth between RAM and CPU Both auto and register storages are sometimes called local identifiers. Storage Classes Static storage duration refers to identifiers whose lifetime is as long as the program’s lifetime. There are two subclasses Static variables are allocated permanent storage in RAM when programs are first loaded into memory. static int N = 5 ; Once a value is assigned to a static storage (N), the value stays even if the function is exited and then re-entered. Any changes in N are also kept until the next reference or modification. Static variables are destroyed (deallocated) only when the program terminates. Storage Classes Example of static storage allocation: #include <stdio.h> int add2 ( void ) ; int main ( ) { int N ; N = add2() ; printf( “%d\n”, N ) ; /* outputs value of N as 2 */ N = add2() ; printf( “%d\n”, N ) ; /* outputs value of N as 4 ! */ return 0 ; } int add2 ( void ) { static int A = 0 ; /* A is initialized to 0 only the first time */ A += 2 ; /* that the function add2 is called. */ return A ; /* Thereafter, 2 is added each time. */ } Storage Classes Extern storage refers to a symbol which, like static, is permanent for the lifetime of the program. Such identifiers permit compilers and linkers to reference identifiers that may be declared inside of other compiled program modules extern int N ; Both static and extern identifiers are sometimes called global identifiers (see next section on Scope) Scope Rules Since symbol names may be referenced only after they have been declared, it follows that there are limits on how and where symbol names may be referenced The Scope of a symbol name refers to the part(s) of a program where the name may be referenced. Scope is sometimes referred to as a name space There are four aspects of symbolic name scope Function scope File scope Block scope Function-Prototype scope Scope Rules – Function scope Variable names that are declared within functions have function scope Such variables may be referenced only within the function in which they are declared References from outside the function generate compiler errors, as if the variable name had never been declared int FuncName ( ...... ) { int A, B ; /* function scope */ float X ; /* function scope */ printf ( “&d &d &f”, A, B, X ) ; } All references to A, B or X must stay “within the box” of the function definition. Scope Rules – Function scope Example: int F1 ( void ) { int X = 5 ; return X ; } int main ( ) { printf ( “%d”, X ) ; return 0 ; } /* Does NOT compile! */ Scope Rules – Function scope Example: int F1 ( void ) { int X = 5 ; return X ; } int main ( ) { int X = 2 ; printf ( “%d”, X ) ; return 0 ; } In effect, two different versions of X exist – each with its own allocated RAM storage location. This can be clarified by thinking of one variable being the “X that exists only inside of F1” while the other is the “X that exists only inside of main”. /* Output: 2 */ Scope Rules – File scope Symbol names can be declared outside of any function Variable names that are declared before function definitions may be referenced directly from within those functions The typical practice is to declare variables within functions, particularly the main function, in order to enforce localization. They are often referred to as Global variables because they can be “globally” recognized by the compiler Global variables have static storage class They are allocated RAM storage locations when the program is loaded and the data stored is persistent for the lifetime of the program itself. Scope Rules – File scope Example: #include <stdio.h> int A = 4 ; /* file scope */ float X = 6.5 ; char C ; int Func1 ( ) { return A + 1 ; } float Func2 ( int Z ) { X=X–Z; return 3.0 * X ; } char Func3 ( ) { return ( C = ‘w’ ); } int main ( ) { int B ; int Y ; B = Func1( ); Func3( ); Y = Func2( X – 0.5 ); printf( “%d %d\n”, A, B ); printf( “%f %f\n”, X, Y ); printf( “%c\n”, C ); } OUTPUT: 4 5 0.5 1.5 w Scope Rules – File scope When to use Global variables There are times when it is most convenient to declare and use global variables for “system” related data Inter-process communication for control Data that is mandated to be treated with utmost care and diligence When to not use global variables Some argue that global variables should never be used This follows the principle of strong localization enforcement Most of the time, use function scoping to control the data flow through the function calling interface This promotes greater clarity and understanding of the logical intent of the programmer and of the algorithm Scope Rules – Block scope Block scope provides a specific programming mechanism for strongly enforcing the principle of localization of variable referencing and algorithmic logic C provides for execution of statements, whether simple or compound. C also provides for separation of compiled code space and static data space in the RAM allocations when the program is loaded. Thus, it is possible to locate variable declaration statements within and amongst executable statements ! This is not necessarily recommended as it can lead to “spaghetti code” that is very difficult to understand Scope Rules – Block scope Example: int main ( ) { int X = 3, K = 10 ; printf( “%d %d\n”, K, X ); { register int K ; for( K = 0 ; K < 4 ; K++ ) { X=X+K; printf( “%d %d\n”, K, X ) ; } } printf( “%d %d\n”, K, X ); return 0 ; } OUTPUT: 10 3 0 3 1 4 2 6 3 9 10 9 Scope Rules – Function-Prototype scope Function-Prototype scope states, simply, that any symbol name declared within the prototype of a function may only be referenced within the prototype This is not the same as a function reference where a symbolic variable name identified in the function interface of the definition is referenced within the function body This may seem a trifle useless at first, but it is a necessary condition of a formal computing language that this case be properly identified and accounted for Otherwise, compilers might not be able to handle certain codes Example: int Func ( float X ) ; /* X is not required! */ int main ( ) { .... } int Func ( float Y ) { .... } /* Y is required */ Lecture 1 : Summary Functions Lecture 1: Summary C Library Functions User Defined Functions Storage Classes and Scope Rules Reading and Review: Chapters 1-5, 9, 13, 14. Assigned Reading: Chapter 5 – C Functions Chapter 6 – C Arrays