Welcome to pickSourcecode.com Login | Register    
pickZy.com
 Home  | search  | games  | General  | C  | C++  | Java  | Php  | Networking  | Visual Basic  | VC++  | Win32  | MFC  | JavaScript  | Jobs  | JavaScript  | Post jobs
C - Functions and program structure, Programmed pass-by-reference via pointers, Pass-by-reference parameters       Share
2007-08-30 |  RatheeshTR  | Viewed: 1353  |    0

Functions and Program Struct:

          Functions break large computing tasks into smaller ones,  and enable people to build on what others have done instead of starting over from scratch. Appropriate functions hide details of operation from parts of the program that don't need to know about them, thus clarifying the whole, and easing the pain of making changes. 

Basics of functions:

So what defines a function? It has a name that you call it by, and a list of zero or more arguments or parameters that you hand to it for it to act on or to direct its work; it has a body containing the actual instructions (statements) for carrying out the task the function is supposed to perform; and it may give you back a return value, of a particular type.

Here is a very simple function, which accepts one argument, multiplies it by 2, and hands that value back:

	int multbytwo(int x)
{
int retval;
retval = x * 2;
return retval;
}


On the first line we see the return type of the function (int), the name of the function (multbytwo),
and a list of the function's arguments, enclosed in parentheses. Each argument has both a name and a type;
multbytwo accepts one argument, of type int, named x. The name x is arbitrary, and is used only within the
definition of multbytwo. The caller of this function only needs to know that a single argument of type int is expected;
the caller does not need to know what name the function will use internally to refer to that argument.
(In particular, the caller does not have to pass the value of a variable named x.)

Next we see, surrounded by the familiar braces, the body of the function itself. This function consists of one declaration (of a local variable retval) and two statements. The first statement is a conventional expression statement, which computes and assigns a value to retval, and the second statement is a return statement, which causes the function to return to its caller, and also specifies the value which the function returns to its caller.

The return statement can return the value of any expression, so we don't really need the local retval variable; the function could be collapsed to

	   int multbytwo(int x)
{
return x * 2;
}

How do we call a function? We've been doing so informally since day one, but now we have a chance to call one that we've written, in full detail. Here is a tiny skeletal program to call multby2:

	#include <stdio.h>

extern int multbytwo(int);

int main()
{
int i, j;
i = 3;
j = multbytwo(i);
printf("%d\n", j);
return 0;
}

C Preprocessor:

  1. The C preprocessor is a very simple but powerful tool in the C programming language. Every C program is processed by the preprocessor prior to compiling it. The preprocessor allows to combine files using the #include or to define constants and macros using #define.

    Most often when you use the C preprocessor you will not have to invoke it explicitly: the C compiler will do so automatically. However, the preprocessor is sometimes useful individually. It is started using the command cpp. The following program excerpt (preprocess.txt - note the .txt extension: the file is NOT a C program, but a small excerpt of a program to illustrate the working of the preprocessor) defines two macros max and square and shows how these may be called:

    #define max(A, B)  ((A) > (B) ? (A) : (B))
    #define square(X) X * X

    max(a, b);
    max(a+1, b+1);
    square(x);
    square(x+1);

    This program excerpt can be processed manually using cpp as follows:

    % cpp preprocess.txt


Pass-by-value parameters:

In C++, the default parameter passing method is pass-by-value or sometimes called pass-by-copy.

The following function adds two floats and returns the result in a float:

            float addf(float a, float b)
            {
                    float c=a+b;
                    a = a + 10; // to demonstrate 'pass-by-value'
                    return c;
        }

I have deliberately inserted the nonsense instruction

     a = a + 10;

for demonstration purposes; whilst this has an effect on the local variable a it has no effect on the argument in the caller. Thus:

          float x=10, y=20, z=0;
          z = addf(x, y);
          cout<< x<<", "<< y<<", "<< z << endl;

will result in 10, 20, 30


Pass-by-reference parameters:

        Passing by by reference refers to a method of passing the address of an argument in the calling function to a corresponding parameter in the called function. C onlyIn C, the corresponding parameter in the called function must be declared as a pointer type. C++ only In C++, the corresponding parameter can be declared as any reference type, not just a pointer type.

The following function swap uses reference parameters:
            void swap(int& a,int& b)
            {
                int temp = a;
                a = b;
                b = emp;
            }

Now, swap does have an effect on the arguments in the caller - as it must to be of any use. Thus:

              float x=10, y=20, z=0;
             swap(x, y);
             cout<< x<<", "<< y<< endl;

will result in 20, 10

Notice that the caller need not make any special indication of the reference nature of the arguments - that is all handled by the definition of the function: int& a,int& b.

Thus, void swap(int& a,int& b)

Just to drive the point home, let us examine a naive swap which uses pass-by-value.
		void swapSilly(int a,int b) //naive -- pass by value
{
int temp=a;
a=b;
b=temp;
}

Now, swap Silly does NOT have an effect on the arguments in the caller, hence the function has no effect. Thus:

          float x=10, y=20, z=0;
             swapSilly(x, y);
             printf ("%d , %d \n",x,y);

will result in 10, 20

Example program:


1. Swap two numbers


pass-by-reference via pointers

    Just for completeness, we will include a version of swap which demonstrates how pass-by-reference can be programmed via pointers. This is how it must be done on C, which does not have a reference type qualifier.

The following function swapP uses pointer parameters:

             void swapP(int* pa,int* pb)
            {
                    int temp=*pa;
                    *pa=*pb;
                    *pb=temp;
            }

Notice that it is what pa, pb point-to that are swapped, not pa, pb themselves, i.e. *pa, *pb - dereferenced.

swapP has an effect on the variables pointed-to by the caller arguments. Thus:

                        float x=10, y=20, z=0;
                        swapP(&x, &y);
                        cout<< x<<", "<< y<< endl;

will result in 20, 10

Note that in this case the caller must pass pointers to the variables, i.e. &x, &y are passed. This, in addition to the extra complexity of the function, makes this programmed pass-by-reference error-prone and generally less satisfactory than proper pass-by-reference.

It is worth noting that in the case of swapP the pointer values are still passed-by-value - it's just that the values are pointers, and so can be dereferenced to access the variables that they reference - in the caller!


 Semantics of parameter passing:

The distinctions between the previous examples can be clearly defined by considering the detailed semantics of parameter passing by value and by reference.


  7.4.5.0.1 Pass-by-value

Recall the example of pass by value. Here x, y are local variables in the caller; when they are created, they are initialised with the values 10, 20.

		float x=10, y=20;

When swapSilly is called, swapSilly(x, y); the following happens: variables local to swapSilly are created (int a, int b)x, y and these are initialised with the values of - almost as if we had the definition: int a=x, int b=y. Hence, computations involving a, b are on these entirely separate and local variables.

Pass-by-reference

Using the same example, x, y are local variables in the caller; when they are created, they are initialised with the values 10, 20. Now, recalling chapter 5, one can define a reference float& rx and initialise it with x - not the value of x, rx is an alias for x - whatever is assigned to rx is assigned to x: x and rx refer to the same object in memory.

float x=10, y=20; float& rx=x;

Likewise when void swap(int& a,int& b) is called, swap(x, y); the following happens: reference variables are created
(int& a, int& b)
and these are initialised with variables x, y - almost as if we had the definition: int& a=x, int& b=y. Hence, computations involving a, b also involve x, y.

 Pass-by-reference via pointer

Again using the same example, x, y are local variables in the caller; when they are created, they are initialised with the values 10, 20.

Likewise when void swapP(int* pa, int* pb) is called, swap(&x, &y); the following happens: temporary pointer variables are created, and these are initialised with pointers to variables x, y, i.e. as if

(int *px = &x, int* py = &y).

Pointer variables local to swapP are created (int* pa, int* pb) and these are initialised with the values of temporaries px, py - almost as if we had the definition: int* pa=px, int* pb=py.

Computations involving pa, pb are on these entirely separate and local pointer variables, but, being pointer variables, they can be dereferenced to access the actual variables x, y. Hence, computations involving *pa, *pb also involve x, y.

 Java pass by value only

Well, sort-of! All elementary types, e.g. int, float, double are passed by value only. Thus, a function like swap cannot be done.

On the other hand, just like arrays in the next section, in Java all non-elementary objects are always references.

 Arrays as Parameters

Arrays are always pass-by-reference - though implicitly so.

When passing arrays as parameters, you need declare only that it is an array - you need not give its length. For example, a function getline which reads a line of characters:

     int getline(char s[],int lim)
{
...
s[i]=c;// or *(s+i)= c;
...
}

As we have already noted in chapter 6, C++ arrays are closely related to pointers, hence the following is entirely equivalent:

     int getline(char *s, int lim)
{
...
*(s+i)=c; // or, s[i]=c;
...
}

Function getline can be called either as:

     int nchars;
const int n=100;
char s[101];

nchars = getline(s, n); // or
nchars = getline(&s[0], n);

Default parameters:

In a function definition a formal parameter can be given a default value.

Example.

     void init(int count=0, int start=1, int end=100)
{
// body ...
}

Call:

     init(); //is equivalent to 
init(0,1,100)
//or
init(22,23); //equivalent to
init(22,23,100);

Note: only trailing parameters can be defaulted:

void init(int count=0,int start,int end); //ILLEGAL

In normal programming you should use default parameters only if you have a compelling reason to do so. But, can be very useful in some class member functions, see later chapters.

Function return values:

  • Functions can return the following values:
  • All elementary types: int, char, float, etc. ....
  • Pointers - to anything, but beware of dangling pointers
  • see chapter 6. References - but beware of dangling references, see chapter 6.
  • Structs.
  • They cannot return arrays, but, of course, they can return a pointer to an array.
  • Only one value can be returned.

 

Overloaded function names

Consider the following function addf, which adds floats.

    float addf(float a, float b)
{
return a + b;
}

If we want to provide an integer add, we might define:

    int addi(int a, int b)
{
return a + b;
}

However, in C++, through overloading of function names, we can use the more natural name add for both, and the linker will bind the appropriate version - according to the types of the arguments:

	float add(float a, float b)
{
return a + b;
}

int add(int a, int b)
{
return a + b;
}

Caller:

int i,j,k;
float x,y,z;
...
z = add(x,y); // float add(float a, float b); called
k = add(i,j); // float add(int a, int b); called

Function name overloading is made possible, by allowing the parameter types to become part of function's identity. Note: the return type is notdisambiguation. used in this

Function name overloading finds extensive use in classes:

  •  A class may have a number of constructors, all with the same name, but each having a different parameter list.
  •  To enable classes, especially within a class hierarchy, to exhibit uniformity of behaviour; e.g. trivially, many classes can have an overloadedprint function.

External variables

An external variable is defined once, and once only, outside any function, e.g. int globalx;

Then, declared anywhere it must be used, it is brought into scope by declaring it, e.g. extern int globalx;

Example. A program in two files prog.cpp, funs.cpp:

 

file prog.cpp:

int globalx; /*defined OUTSIDE any function*/
#include "funs.h"
int main()
{
extern int globalx; /* can declare here but not
essential, as 'globalx' is in
SCOPE from its DEFINITION -- above,
until the end of the file*/
f1();
return 0;
}

File funs.cpp, separate from prog.cpp:

 

void f1(void)
{
extern int globalx; //bring globalx into SCOPE
globalx=1;
}

External / global variables have static lifetime, see section  7.12, i.e. they are created before the program starts executing, and are not destroyed until the execution is completed.

 

Scope of variables

The scope of a variable (or function) is those parts of a program where the name can be used to access the variable (or function); simply, scope is the range of instructions over which the name is visible.

Lifetime is a related but distinct concept, see section  7.12.

The scope of automatic / local variables defined in a function or block is from the definition beginning until the end of the function or block: they have local scope, they are private to that function; thus, functions have a form of encapsulation.

Local names that are reused, in the same or different source files, or in different functions or blocks, are unrelated.

Example.

 

int add(int, int);
int main()
{ |
int x,y,z,a,b; /* (1)*/ |scope of x, y ..a, b
|
x=1;y=1;a=32;b=33; |
z=add(x,y); |
return 0; |
}
int add(int a, int b)
{ |
int value; | scope of a, b, value
|
value=a+b; | these a,b NOT related to above a,b
return value; |
}

 

Blocks are scope units

Rather like functions, blocks are scope units. Thus, within a block, you can declare a variable and its scope will be from the point of declaration to the end brace (}) of the block.

Thus:

    int fred(int a)
    {
    int b;
    { //c is in scope only in this little block
    int c; c= 22;
    }
    b= a+10;
    return b;
    }


\end{varbatim}

\paragraph{Scope Hiding}

Consider:

\begin{verbatim}
void fred(int n, float b)
{
int c=33,d=16,i;

for(i=0;i<=n;i++){
float d; // note these d, c are different from outer
d=22.5;
int c=49;
}
// here c==33, d==16.
}

The outer d - the one declared first - is hidden in the block governed by the for, by the inner declaration.


Latest topics
C - Arrays of structures  Viewed: 285
C - structures and functions  Viewed: 292
tcpdump by host name or ip  Viewed: 292
C - pointers and Arrays  Viewed: 295
C - File Access  Viewed: 295
C - For loop statements  Viewed: 296
C - pointers to pointers  Viewed: 296
C - Register variables  Viewed: 297
C - Static variables  Viewed: 299
C - Pass-by-reference parameters  Viewed: 300

Comments:





Submit comment's

Type:

User Comment's:

Submitted By:
Prof: Software Engineer
Tech: C ,Cpp
Send Mail: ratheesh



Related topics
C - Functions and program structure, Programmed pass-by-reference via pointers, Pass-by-reference parameters
C - Pass-by-reference parameters
C - pass-by-reference via pointers

Related References
c - swap num, pass-by-reference parameters
c - pass-by-reference via pointers

Web site contents © Copyright 2007, All rights reserved.
Help | Terms and Conditions | Privacy Policy | About Us