Call by reference using pointers

December 19th, 2009 by Kevin | No Comments | Filed in C Programming

We have seen that in a C program, the function arguments are call by value by default. Which means that when we pass arguments into a function, a copy of those arguments is created instead of using the actual variables which have been passed.

void foo(int x, int y)
{
   x = 50; /* does not affect the value of a because x is copy of a */
   y = 100; /* does not affect the value of b because y is copy of b */
   return;
}

void main()
{
   int a = 10;
   int b = 20;

   foo(a, b); /* the actual arguments passed to the function are a and b */
   printf("a = %d, b = %d\n"); /* after calling function foo(), value of a is 10 and b is 20 */
   return;
}

So if we need to get the values of a and b modified by the function foo(), we need to use call by reference. Formally, there is no mechanism in C for call by reference. But pointers help us out by providing this functionality.

void foo(int* px, int* py)
{
   *px = 50; /* since px is pointing to a, *px is reference to a */
   *py = 100; /* since py is pointing to b, *py is reference to b */
   return;
}

void main()
{
   int a = 10;
   int b = 20;

   foo(&a, &b); /* the actual arguments passed to the function are a and b */
   printf("a = %d, b = %d\n"); /* after calling function foo(), value of a is 50 and b is 100 */
   return;
}

In the above example, we pass the address of variables a and b to the function foo() which accepts pointers to integer as arguments. So when we reference the variables by using the dereference operator on px and py, we are referencing the actual variables a and b. This causes their value to be modified in the calling function.

This is further simplified in the figure shown below:

Call by reference

An array is handled specially when it is passed as an argument. Instead of copying the entire array, just a pointer to the first element of the array is passed as a parameter (which in fact is passing the array by reference). So any changes made to the array passed as a formal argument will reflect into the actual argument.

void foo(int pArray[]) /* array passed as call by reference. could also be int *pArray */
{
   int i = 0;
   for(i=0; i<10; i++)
   {
      pArray[i] = 100; /* changes the value of array a in the main() */
   }
   return;
}

void main()
{
   int a[10];

   foo(a); /* the actual arguments passed to the function are a and b */
   return;
}
 

Related Posts:

Tags: ,

Pointers and Arrays

December 18th, 2009 by Kevin | No Comments | Filed in C Programming

Array

An array is a collection of items which share the same characteristics. So we can have an array of integers, array of characters, etc. The important point about arrays is that the elements of the array are located into contiguous locations of memory. This can be used to our advantage in programming.

An integer array is defined as int a[5];

Array

As the diagram above shows, we have defined an array of type integer with six elements in it. The elements of the array are stored sequentially in the memory as shown. Hence they can be accessed by their index as a[0](first element), a[1](second element), a[2], …

Since this array is of type integer, every element is an integer and so each occupies four bytes (depends on the machine) of memory.

A character array is a special kind of array which is also known as a string. What makes the character array special is that it can be used by several functions in the string manipulation library (and also by the user defined functions) because of it’s characteristic of placing a ‘\0′ escape sequence at the end of the array to indicate the string termination.

Pointers

A pointer is a variable which holds the memory address of another variable. We can have a pointer to any data type we want such as integers, floats, characters, etc. We can also have pointers to other abstract data types that we create (which we will see in later posts).

Pointer Variables

As shown in the figure, p is a pointer variable which points to the address of an integer variable a. The following example will make it more clear.

int *p; /* this is the declaration of the integer pointer. */
int a = 10;

p = &a; /* Here the address of variable a is assigned to pointer p by using the reference operator */
printf("a = %d\n", *p); /* Here a is shown to be same as *p where * is the dereference operator */

And then the pointer can be assigned the address of any integer variable as

p = &a;

& is the referencing symbol and * is the de-referencing symbol for the pointer.

Pointers and Arrays

An array is a sequence of elements stored in a contiguous block of memory. So we can consider an array a of 10 elements by representing it with a pointer as shown

int a[10];

int *p = &a[10]; /* pointer to an array */

a[5] = 10;

*(p + 5) = 10; /* same as above statement */

p[5] = 10; /* same as above two statements */

When an array is passed into a function, instead of a copy of the array just a unmodifiable pointer to the first element of the array is passed.

In the next post, I will write about such uses of pointers including others such as call by reference, function pointers.

 

Related Posts:

Tags: , ,

The C Preprocessor and it’s uses

December 16th, 2009 by Kevin | 4 Comments | Filed in C Programming

As can be seen from The C compilation model, before the compiler is called, the C program is passed through a preprocessor. The basic job of the C preprocessor is to replace every occurrence of what we call a macro with the string that it represents. Below are the different functionality provided by the C preprocessor.

#include

The #include preprocessor directive tells the preprocessor to include whatever file is named after it into the current file. There are two methods to use #include as #include “filename” or #include <filename>. The difference is that in the former case, the preprocessor will search for the file in the current directory and if it is not present will search in the standard directory(whose path is set in the system eg: /usr/local/include) and in the latter case, it will search only in the standard directory.

Modular structure of a C program

The header file is a good concept to separate the variable and function declarations which need to be used by another file or many files as seen above. This can be seen in Modular structure of a C program.

One question that I have wondered about is that is it possible to include any file using #include? Generally, we just include header files in a C program as seen above. What happens if I use something like #include “foo.c”?

The answer is yes we can include a C file into another C file. But it is also suggested not to do so. One reason is that generally when development environments compile a project, they compile all the C files and create .obj files. So you might end up with two obj files containing the same symbols and will get a linker error.

#define

The #define macro is used to define a replacement text. So when the preprocessor will parse the C file, it will replace all instances of the macro with the definition that it represents. The usage is:

#define name replacement_text

#define MAX 1000

int main()
{
   int a = 20;
   int b = a + MAX;        /* after the preprocessing this statement changes to "int b = a + 1000"; */
   printf("b = %d\n", b); /* This prints b = 1020 */
   return 0;
}

#define can also be used along with arguments. A typical example will make this clear.

#define MAX(a, b) (a > b) ? a : b

int main()
{
   int a = 100;
   int b = 200;
   printf("Max = %d\n", MAX(a, b));   /* prints Max = 200 */
   return 0;
}

Care has to be taken when using such arguments because the #define macro leads to the expansion of the definition. So,

#define square(x) x*x
used as
square(z+1)
expands to z+1*z+1 which is wrong. We should use parenthesis instead as
#define square(x) ((x)*(x))

The “#” symbol is used to replace the argument with the name of the argument.

#define print(x) printf("#x = %d\n", x)
print(varText) will expand to printf("varText = %d\n", varText);

This function is great for printing in any logging functionality.

The ‘##’ preprocessor symbol can be used to concatenate two arguments placed next to each other in the macro.

#define combine(name, number) name ## number
combine(table, 1)    /* this will expand to "table1" */

#if, #ifdef

#if enables us to use conditional checking in a preprocessor.

#if (SYSTEM == WINDOWS)
   #include"windows.h"
#elif (SYSTEM == UNIX)
   #include "unix.h"
#else
   #include "linux.h"
#endif

There is a special way to check if a header file is already included. Suppose we have a header sys_header.h.

system.h

#include "sys_header.h"

main.c

#include "system.h"
#include "sys_header.h"

This will cause the problem of duplicate definitions because sys_header.h gets included two times, one already in system.h and another one explicitly. This can be avoided by placing the following in sys_header.h. Infact, this is a good programming practice to follow in any header file.

#ifndef SYS_HEADER_H
#define SYS_HEADER_H

...

#endif /* SYS_HEADER_H */

What this does is that when we include sys_header.h, it will first check if SYS_HEADER_H macro is defined or not. If we have already included sys_header.h, then the SYS_HEADER_H will be defined and so it will not be included again.

#error

The #error macro is used by the preprocess to indicate an error condition and to print out that error on the screen.

#define BUFFER_SIZE 255

#if BUFFER_SIZE < 256
#error "BUFFER_SIZE is too small."
#endif

This will cause the compilation to stop and print the message on the screen.

 

Related Posts:

Tags: ,