CS 201 Lecture No 10



  Lecture Handout
  Introduction to Programming
  Lecture No. 10
  Reading Material
  Deitel & Deitel – C++ How to Program    Chapter 3 
  3.7, 3.11, 3.12, 3.14, 3.17
  Contents
    Header Files
    Scope of Identifiers
    Functions
  -  Call by Value 
  -  Call by Reference
  Header Files
  You have already been using a header file from day-zero. You know that we used to
  write at the top before the start of the   main() function <iostream.h>, with ‘.h’ as an
  extension, you might have got the idea that it is a header file.
  Now we will see why a Header file is used.
  In the previous lecture, we discussed a little bit about Function Prototypes. One thing is
  Declaration and other is Definition. Declaration can also be called as 'Prototype'. 
  Normally, if we have lot of functions and want to use them in some other function or
  program, then we are left with only one way i.e. to list the prototypes of all of them
  before the body of the function or program and then use them inside the function or
  program. But for frequent functions inside a program, this technique increases the
  complexity (of a program). This problem can be overcome by putting all these function
  prototypes in one file and writing a simple line of code for including the file in the
  program. This code line will indicate that this is the file, suppose 'area.h' containing all
  the prototypes of the used functions and see the prototypes from that file. This is the basic
  concept of a header file. 
  So what we can do is: 
  -  Make our own header file which is usually a simple text file with '.h' extension ('.h'
  extension is not mandatory but it is a rule of good programming practice).
  -  Write function prototypes inside that file. (Recall that prototype is just a simple line
  of code containing return value, function name and an argument list of data types
  with semi-colon at the end.)
  -  That file can be included in your own program by using the ‘#include’ directive and
  that would be similar to explicitly writing that list of function prototypes.
  Function prototypes are not the only thing that can be put into a header file. If you
  remember that we wrote a program for calculating Area of a Circle in our previous
  lectures. We used the value of 'pi' inside that and we have written the value of 'pi' as
  3.1415926. This kind of facts are considered as Universal Constants or Constants within
  our domain of operations . It would be nice, if we can assign meaningful names to them.
  There are two benefits of doing this. See, We could have declared a variable of type
  double inside the program and given a name like 'pi':
  double  pi  =  3.1415926;
  Then everywhere in the subsequent calculations we can use 'pi'.
  But it is better to pre-define the value of the constant in a header file ( one set for all) and
  simply including that header file, the constant ‘pi’, is defined. Now, this meaningful
  name ‘pi’ can be used in all calculations instead of writing the horrendous number
  3.1415926 again and again. 
  There are some preprocessor directives which we are going to cover later. At the
  moment, we will discuss about ‘#define’ only. We define the constants using this
  preprocessor directive as:
  #define pi  3.1415926
  The above line does a funny thing as it is not creating a variable. Rather it associates a
  name with a value which can be used inside the program exactly like a variable. (Why it
  is not a variable?, because you can’t use it on the left hand side of any assignment.). 
  Basically, it is a short hand, what actually happens. You defined the value of the ‘pi’ with
  ‘#define’ directive and then started using ‘pi’ symbol in your program. Now we will see
  what a compiler does when it is handed over the program after the writing process.
  Wherever it finds the symbol ‘pi’, replaces the symbol with the value 3.1415926 and
  finally compiles the program. 
  Thus, in compilation process the symbols or constants are replaced with actual values of
  them. But for us as human beings, it is quite readable to see the symbol ‘pi’. Additionally,
  if we use meaningful names for variables and see a line ‘2 * pi * radius’, it becomes
  obvious that circumference of a circle is being calculated. Note that in the above
  statement, ‘2 * pi * radius’; 2 is used as a number as we did not define any constant for it.
  We have defined ‘pi’ and ‘radius’ but defining 2 would be over killing.
  Scope of Identifiers
  An 'Identifier' means any name that the user creates in his/her program. These names can
  be of variables, functions and labels. Here the scope of an identifier means its visibility.
  We will focus Scope of Variables in our discussion. 
  Suppose we write the function:
  void func1()
  {
  int i;
  . . .   //Some other lines of code
  int j = i+2;   //Perfectly alright
  . . .
  }
  Now this variable ‘i’ can be used in any statement inside the function func1(). But
  consider this variable being used in a different function like:
  void func2()
  {
  int k = i + 4; //Compilation error
  . . .
  }
  The variable ‘i’ belongs to func1() and  is not visible outside that. In other words, ‘i’ is
  local to func1(). 
  To understand the concept of scope further, we have to see what are   Code Blocks? A
  code block begins with ‘{‘ and ends with ‘}’.Therefore, the body of a function is
  essentially a code block. Nonetheless, inside a function there can be another block of
  code like 'for loop' and 'while loop' can have their own blocks of code respectively.
  Therefore, there can be a hierarchy of code blocks. 
  A variable declared inside a code block becomes the local variable for that for that block.
  It is not visible outside that block. See the code below:
  void func()
  {
  int outer;   //Function level scope
                          . . .
  {
  int inner;   //Code block level scope
  inner = outer;    //No problem
  . . .
  }
  inner ++;   //Compilation error
  }
  Please note that variable ‘outer’ is declared at function level scope and variable ‘inner’ is
  declared at block level scope.
  The ‘inner’ variable declared inside the inner code block is not visible outside it . In other
  words, it is at inner code block scope level. If we want to access that variable outside its
  code block, a compilation error may occur.
  What will happen if we use the same names of variables at both function level scope and
  inner block level scope? Consider the following code:
  Line
  1.         void increment()
  2.   {
  3.   int num;   //Function level scope
  4.   . . .
  5.   {
  6.   int num;   //Bad practice, not recommended
  7.                                 . . .
  8.   num ++;   //inner num is incremented
  9.                                 . . .
  10.   }
  11.   }
  Note that there is no compilation error if the variable of the same name ‘num’ is declared
  at line 6 inside the inner code block (at block level scope). Although , there is no error in
  naming the variables this way, yet this is not recommended as this can create confusion
  and decrease readability. It is better to use different names for these variables.
  Which variable is being   used at line 8? The answer is the ‘num’ variable declared for
  inner code block (at block level scope). Why is so? It is just due to the fact that the outer
  variable ‘num’ (at function level scope) is hidden in the inner code block as there is a
  local variable of the same name. So the local variable ‘num’ inside the inner code block
  over-rides the variable ‘num’ in the outer code block.
  Remember, the re-use of a variable is perfectly alright as we saw in the code snippet
  above while using ‘outer’ variable inside the inner code block. But re-declaring a variable
  of the same name like we did for variable ‘num’ in the inner code block, is a bad practice. 
  Now, is there a way that we declare a variable only once and then use it inside all
  functions. We have already done a similar task when we wrote a function prototype
  outside the body of all the functions. The same thing applies to declaration of variables.
  You declare variables outside of a function body (so that variable declarations are not
  part of any function) and they become visible and accessible inside all functions of that
  file. Notice that we have just used a new word ‘file’. 
  A file or a source code file with extension ‘.c’ or ‘.cpp’ can have many functions inside.
  A file will contain one   main() function maximum and rest of the functions as many as
  required. If you want a variable to be accessible from within all functions, you declare the
  variable outside the body of any function like the following code snippet has declared
  such a variable ‘size’ below.
  #include <iostream.h>
  . . .
  // Declare your global variables here
  int size;
  . . .
  int main( … )
  {
  . . .
  }
  Now, this ‘size’ is visible in all functions including   main(). We call this as 'file scope
  variable' or a 'global variable'. There are certain benefits of using global variables. For
  example, you want to access the variable ‘size’ from anywhere in your program but it
  does have some pitfalls. You may inadvertently change the value of the variable ‘size’
  considering it a local variable of the function and cause your program to behave
  differently or affect your program logic.
  Hence, you should try to minimize the use of global variables and try to use the local
  variables as far as possible. This philosophy leads us to the concept of Encapsulation and
  Data Hiding that encourages the declaration and use of data locally.
  In essence, we should take care of three levels of scopes associated with identifiers:
  global scope, function level scope and block level scope. 
  Let's take a look of very simple example of global scope:
  #include <iostream.h>
  //Declare your global variables here
  int i;
  void main()
  {
  i = 10;
  cout << “\n” << “In main(), the value of i is: “ << i;
  f();
  cout << “\n” << “Back in main(), the value of i is: “ << i;
  }
  void f()
  {
  cout << “\n” << ”In f(), the value of i is: “ << i;
  i = 20;
  }
  Note the keyword ‘void’ here, which is used to indicate that this function does not return
  anything.
  The output of the program is:
  In main(), the value of i is: 10
  In f(), the value of i is: 10
  Back in main(), the value of i is: 20
  Being a global variable, ‘i’ is accessible to all functions. Function f() has changed its
  value by assigning a new value i.e. 20. 
  If the programmer of function f() has changed the value of ‘i’ accidentally taking it a
  local variable, your program’s logic will be affected. 
  Function Calling
  We have already discussed that the default function calling mechanism of C is a 'Call by
  Value'. What does that mean? It means that when we call a function and pass some
  arguments (variables) to it, we are passing a copy of the arguments (variables) instead of
  original variables. The copy reaches to the function that uses it in whatever way it wants
  and returns it back to the calling function. The passed copy of the variable is used and
  original variable is not touched. This can be understood by the following example. 
  Suppose you have a letter that has some mistakes in it. For rectification, you depute
  somebody to make a copy of that letter, leave the original with you and make corrections
  in that copy. You will get the corrected copy of the letter and have the unchanged original
  one too. You have given the copy of the original letter i.e. the call by value part. 
  But if you give the original letter to that person to make corrections in it, then that person
  will come back to you with the changes in the original letter itself instead of its copy.
  This is call by reference.
  The default of C is 'Call by Value'. It is better to use it as it saves us from unwanted side
  effects. Relatively, 'Call by Reference' is a bit complex but it may be required sometimes
  when we want the actual variable to be changed by the function being called. 
  Let's consider another example to comprehend 'Call by Value' and how it works. Suppose
  we write a main() function and another small function f(int) to call it from main(). This
  function f( ) accepts an integer, doubles it and returns it back to the main() function. Our
  program would look like this:
  #include <iostream.h>
  void f(int);   //Prototype of the function
  void main()
  {
  int i;
  i = 10;
  cout << “\n” << ” In main(), the value of i is: “ << i;
  f(i);
  cout << “\n” << ” Back in main(), the value of i is: “ << i;
  }
  void f (int i)
  {
  i *= 2;
  cout << “\n” << “ In f(), the value of i is: “ << i;
  }
  The output of this program is as under:
  In main(), the value of i is: 10
  In f(), the value of i is: 20
  Back in main(), the value of i is: 10
  As the output shows the value of the variable ‘i’ inside function main() did not change, it
  proves the point that the call was made by value. 
  If there are some values we want to pass on to the function for further processing, it will
  be better to make a copy of those values , put it somewhere else and ask the function to
  take that copy to use for its processing. The original one with us will be secure.
  Let's take another example of call by value, which is bit more relevant. Suppose we want
  to write a function that does the square of a number. In this case, the number can be a
  double precision number as seen below:
  #include  <iostream.h>
  double square (double);
  void main()
  {
  double num;
  num = 123.456;
  cout << “\n” << “ The square of  “ << num << “ is “ << square(num);
  cout << “\n” << “ The current value of num is “ << num;
  }
  double square (double x)
  {
  return x*x;
  }
   'C' does not have built-in mathematical operators to perform square, square root, log and
  trigonometric functions. The C language compiler comes along a complete library for
  that. All the prototypes of those functions are inside ‘<math.h>’. In order to use any of
  the functions declared inside ‘<math.h>’, the following line will be added.
  #include <math.h>
  Remember, these functions are not built-in ones but library is supplied with the C-
  compiler. It may be of interest to you that all the functions inside ‘<math.h>’ are called
  by value. Whatever variable you will pass in as an argument to these functions, nothing
  will happen to the original value of the variable. Rather a copy is passed to the function
  and a result is returned back, based on the calculation on that copy.
  Now, we will see why Call by Reference is used.
  We would like to use 'call by reference' while using a function to change the value of the
  original variable. Let's consider the square(double) function again, this time we want the
  original variable ‘x’ to be squared.  For this purpose, we passed a variable to the square()
  function and as a result, on the contrary to the ‘Call by Value’, it affected the calling
  functions original variable. So these kinds of functions are ‘Call by Reference’ functions.
  Let us see, what actually happens inside Call by Reference?
  As apparent from the name ‘By Reference’, we are not passing the value itself but some
  form of reference or address. To understand this, you can think in terms of variables
  which are names of memory locations. We always access a variable by its name (which
  in fact is accessing a memory location), a variable name acts as an address of the memory
  location of the variable. 
  If we want the called function to change the value of a variable of the calling function, we
  must pass the address of that variable to the called function. Thus, by passing the address
  of the variable to the called function, we convey to the function that the number you
  should change is lying inside this passed memory location, square it and put the result
  again inside that memory location. When the calling function gets the control back after
  calling the called function, it gets the changed value back in the same memory location. 
  In summary, while using the call by reference method, we can’t pass the value. We have
  to pass the memory address of the value.  This introduces a new mechanism which is
  achieved by using ‘&’ (ampersand) operator in C language. This ‘&’ operator is used to
  get the address of a variable. Let's look at a function, which actually is a modification of
  our previous square() function.
  #include  <iostream.h>
  void square(double);
  void main()
  {
  double x;
  x = 123.456;
  cout << “\n” << “ In main(), before calling square(), x = “ << x;
  square(&x);   //Passing address of the variable x
  cout << “\n” << “ In main(), after calling square(), x = “ << x;
  }
  void square(double* x)   //read as: x is a pointer of type double
  {
  *x  =  *x  *  *x;   //Notice that there is no space in *x
  }
  Here *x means whatever the x points to and &x means address of the variable x. We will
  discuss Pointers in detail later.
  We are calling function square(double*) with the statement square(&x) that  is actually
  passing the address of the variable x , not its value. In other words, we have told a box
  number to the function   square(double*) and asked it to take the value inside that box,
  multiply it with itself and put the result back in the same box. This is the mechanism of
  ‘Call by Reference’.
  Notice that there is no return statement of square(double*) as we are putting the changed
  value (that could be returned) inside the same memory location that was passed by the
  calling function.
  The output of the program will be as under:
  In main(), before calling square(), x = 123.456
  In main(), after calling square(), x = 15241.4
  By and large, we try to avoid a call by reference. Why? Mainly due to the side-effects, its
  use may cause. As mentioned above, it will be risky to tell the address of some variables
  to the called function. Also, see the code above for some special arrangements for call by
  reference in C language. Only when extremely needed, like the size of the data to be
  passed as value is huge or original variable is required to be changed, you should go for
  call by reference, otherwise stick to the call by value convention. 
  Now in terms of call by reference, we see that there are some places in ‘C’ where the call
  by reference function happens automatically. We will discuss this later in detail.  For the
  moment, as a hint, consider array passing in ‘C’.
  Recursive Function
  This is the special type of function which can call itself. What kind of function it would
  be?  There are many problems and specific areas where you can see the repetitive
  behavior (pattern) or you can find a thing, which can be modeled in such a way that it
  repeats itself.  
  Let us take simple example of x10, how will we calculate it? There are many ways of
  doing it. But from a simple perspective, we can say that by definition x10 = x * x9. So
  what is x9? It is x9 = x * x8 and so on. 
  We can see the pattern in it:
  xn = x * xn-1
  To compute it, we can always write a program to take the power of some number. How to
  do it? The power function itself is making recursive call to itself. As a recursive function
  writer, you should know where to stop the recursive call (base case). Like in this case,
  you can stop when the power of x i.e. n is 1 or 0.
  Similarly, you can see lot of similar problems like Factorials. A factorial of a positive
  integer ‘n’ is defined as:
  n! = (n) * (n-1) * (n-2) * ….. * 2 * 1
  Note that 
  n! = (n) * (n-1)!
  and    (n-1)! = (n-1) * (n-2)!
  This is a clearly a recursive behavior. While writing a factorial function, we can stop
  recursive calling when n is 2 or 1. 
  long fact(long n)
  {
  if (n <= 1)
  return 1;
  else
  return  n * fact(n-1);
  }
  Note that there are two parts (branches) of the function: one is the base case ( which
  indicates when the function will terminate) and other is recursively calling part.
  All the problems can be solved using the iterative functions and constructs we have
  studied until now. So the question is: do we need to use recursive functions? Yes, it adds
  little elegance to the code of the function but there is a huge price to pay for this.  Its use
  may lead to the problems of having memory overhead. There may also be stacking
  overhead as lots of function calls are made. A lot of functions can be written without
  recursion (iteratively) and more efficiently. 
  So as a programmer, you have an option to go for elegant code or efficient code,
  sometimes there is a trade-off. As a general rule, when you have to make a choice out of
  elegance and efficiency, where the price or resources is not an issue, go for elegance but
  if the price is high enough then go for efficiency.
  ‘C’ language facilitates us for recursive functions like lot of other languages but not all
  computer languages support recursive functions. Also, all the problems can not be solved
  by recursion but only those, which can be separated out for base case, not iterative ones.
  Tips
  -  Header file is a nice mechanism to put function prototypes and define constants
  (global constants) in a single file. That file can be included simply with a single line
  of code.
  -  There are three levels of scopes to be taken care of, associated with identifiers: global
  scope, function level scope and block level scope.
  -  For Function calling mechanism, go for ‘Call by Value’ unless there is a need of ‘Call
  by Reference’.
  Apply the recursive function where there is a repetitive pattern, elegance is required
  and there is no resource problem.

Post a Comment

Don't Forget To Join My FB Group VU Vicky
THANK YOU :)

Previous Post Next Post