Sunday, July 13, 2008

Read these C statements

You think you have understood pointers in C. And written some programs also using pointers. Then tell me what are these
1> int *a
2> int **a
3> int *a()
4> int (*a)()
5> int *a[5]
6> int (*a)[5]
7> int (*a())[5]



First one is obvious. All of us know a is a pointer to an integer.

<2> is again simple. a is a pointer to pointer to an integer

<3> Here a is a function whose return type is int pointer

<4> a is pointer to function which has no parameters and which returns an integer. Now you know how to write function pointers. Remeber to enclose the * along with function name in paranthesis.

<5> a is an array of 5 integer pointers

<6> a is pointer to an array of 5 integers. This is equivalent to a 2 dimensional array. Now do some home work. Compile a program with a function which takes a 2d array as a parameter (say
void print(int a[3][3])
with debugging enabled (as gcc prg.c -g). Start gdb and enter the function. Now ask the gdb whatis 2d array
gdb> whatis a
type int (*a)[3]

So gdb is telling you your parameter is a pointer to an array. This is one concept many of us misunderstand. Since 1d array gets converted into pointer when passed as a parameter, we think 2d array gets converted into pointer to pointer. NO, IT DOES NOT. It gets converted into a pointer to an 1d array, this pointer pointing to the first row. By incrementing the pointer, we get the second row.

<7> Here is a function which returns a 2d array. Now you will think how can a function return an array? No, the function is returning a pointer to an array, which is nothing but address of first row of the 2d array.
Let us write a function to return a matrix after doubling it and without modifying the original matrix

int (*doubleElements(int c[3][3])[3]
{
int d[3][3];
int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
d[i][j]=c[i][j]*2;
return b;
}

Please note that int d[3][3] can also be written as int (*d)[3].

Now the more difficult part is how to accept the matrix returned by this function.

int a[3][3],b[3][3];
-------
-------
b=doubleElements(a);
-------
-------

Now tell me why this wont work. b is the base address of the array and hence can not be used on LHS. So let us rewrite it as
int a[3][3], (*b)[3];

Fine we are ready to test the program.Do we get the output. NO. We get some garbage values. Why?

Look at the life span of d. This array is valid as long as the function exists. And is popped out the moment you return from function. It is destroyed and we are returning this destroyed array.
Only way to overcome this problem is declare d as a static array.
static int (*d)[3];

Now static elements are not stored in data segment and hence are not destroyed when function returns.And now our prgram works!!!

Thursday, July 3, 2008

Functions (2)




Functions have to be declared, defined and called. You remember #include in all your programs. That stdio.h file contains the declarations of many of your library function. (Note not defintion but just declaration).




Exercise 1: Search for the file stdio.h and see the contents of the file

A function declaration as well as the header of the function must specify the name of the function, parameter list and also return type. If you do not mention the return type, c compiler assumes that the function returns an int.

float findHalf(int n)
{
return (n/2.0);
}
Now define this function after defintion of main(). Now call this function say



printf("%f",findHalf(15));
What do answer do you obtain? 7.5? 7? No. Answer will be zero.



The compiler does not know about the function(it does not see declaration or definition before calling the function) and thinks it is an int function. Wrong format specifier %f for int. Hence the wrong answer.



(Please note that, if the function is defined before main(), then the function definition itself serves the purpose of declaration. )



Now declare the function as
findHalf(int );

You don't see change in the output. Again since you haven't mentioned return type, compiler thinks it is an int function.

float findHalf(int );

This declaration gives you the correct answer.

Ex 2: Write a program to print how many days are left for you on this earth making an assumption that you live upto 80 years. OK, ok, if you think it is too morbid, write a program to print how many days old are you.

Let us write some bit manipulation programs which Ritchie is so fond of. First write a function to find the number of 1 bits in the given int.


int numBits(unsigned int n )

{

int c=0;

while(n)

{

if (n&1)

c++;

n=n>>1;

}

return c;

}



But one of Ritchie's exercises asks you to solve this program without using loop. He has also given hint. Try it.






You may want to know where are the variables within the function stored. All the local variables are stored in the stack. Once a function is called, a new stack frame is created. This stack frame has all local variables, parameters and return address (which statement to execute after returning from the function), temporaries, saved registers, place for return value etc. The function accesses the values from the top most stack frame. Hence a function can not access variables defined in other functions including main()












Now look at this image fro http://www.oaklib.org/ which shows the ways stack frames are organised when 3 functions are being called.


In Linux OS, compile your program with the option -g (enables debugging). Start the gdb and single step through the program and observe the stack frames using bt.




bt can also be used in understanding the recursive functions better. A recursive function is the one which calls itself. Finding the factorial is most taught recursive function.

int factorial(int n)

{

if (n)

return (n*factorial(n-1);

else

return 1;

}

Remember the following rules when writing a recursive function. The function should not call recursion for atleast one value and other values must proceed towords that value. (Here recursion is not called for 0 and n is getting reduced ) If this condition is not met, the function will be infinitely recursive. Here if return (n*factorial(n-1); only this statement is used then the function will never end.

Don't be tempted make the function recursive unnecessarily. Recursive function create many stack frames , one frame for each call and hence eat up memory. This makes the program slow as well. You should try to avoid such situations, especially when you have limited memory e.g. embedded systems.

The parameters sent to the function are copied to the stack and these copies will be manipulated. Original parameters are unaltered.

int doubleNum(int n)

{

n=n<<1;

}

int main()

{

int n = 10; doubleNum(n);printf("%d",n);//will not print 20

}


The function modifies the local copy of n and does not modify n in main. If you really need to change the parameter, then send the address of variable ie a pointer