C Pointers

Pointers provide a mechanism for the direct manipulation of memory. They are arguably the most powerful, and the most dangerous, feature of the C programming language.

A pointer is a variable that contains the address of a variable.

Computer Memory Layout

Before digging deep into pointers, lets look at memory orgranization in computer.

A typical machine has an array of consecutively numbered memory cells. These numbers are termed addresses. Each cell consists of a set of bits, and the cell bit-pattern is the cell’s value. This concept can be depicts in figure below:

pointers

Figure: Conceptual representation of memory management in computer

When a variable is defined, specific memory is allocated to store it's value. Thus, the variable has a value and an address for where that value resides.

For an example, let x be defined and initialised to the value 3.

int x = 3;

Assume this variable is stored at address 92 as shown in figure above.

Now, let us define a pointer px, and lets assume it is stored at address 53, and initialised with the address of x as follows.

int *px; //px is a pointer to int
px = &x; //px now point to x i.e. address of a variable is obtained using the “address-of” operator &.

The situation has been depicted on fibure above. And it is clear that the value of px is 93.

The size of a pointer type determines the maximum value it can represent, and hence the maximum range of addresses it can deal with. For example, a 16-bit pointer can only handle addresses between 0 and 216 −1 (i.e., 65535). 

 

Pointer Syntax

The value of the variable to which a pointer points can be obtained using the dereferencing operator *.

int i = 4;
int *j = &i;  /* Defines a pointer-to-int j, and initialise with address of i. */
int x = *j;   /* x is assigned the value of i (that is, 4). */

The dereferencing use of * should not be confused with its use in pointer-declaration syntax. The declaration *, meaning “is a pointer-type variable” occurs only in variable or function declarations, and in all other circumstances the * means dereference or “access the pointed-to object”.

Pointer Arithmetic

Each variable type has a corresponding pointer type. This allows the compiler to automatically calculate the byte-offset required for indexing an array of that type.

If px point to integer x, then *px can occur in any context where x could, so

*px = *px + 10;

increments *px by 10 and px point to 10th element after px.

In general, px += n increments it point n elements beyond where it did originally.

Similarly, the following arithmetic operations are also ok with pointers.

++ *px;   //pre-increment
(*px)++;  //post-increment

Pointers and array

Let pa be a pointer to an integer declared as,

int *pa;

and 

int a[10];

be an array with 10 elements as discussed in earlier tutorial Arrays in C Programming.

Then the assigment below sets pa to point to element zero of a; that is, pa contains the address of a[0].

This scenario can be depicted as below:

pointers-array

Then, the following assignment will copy contents of a[0] into x.

x = *pa;

Also, pa + i points to i elements after pa and pa – i points to i elements before. So, if pa points to a[0], * ( pa +1) points to a[1], and *(pa+i) points to contents of a[i]

pointers-array-ath

Pass By reference

It is always passed by value when a variable is passed to a function. That is, the variable is copied to the formal parameter of the function argument list. As a result, any changes made to the local variables within the function will not affect the variables of the calling function.

For example, lets take an example program to swap two numbers.

#include<stdio.h>

void swap(int x, int y)  /* x and y are copies of the passed arguments. */
{
    int tmp = x;         /* The variable x is unrelated to the variable num1 */
    x = y;               /* so this operation does not affect num1. */
    y = tmp;
}

int main(){
    int num1 = 4, num2 =9;
    printf("Before swap num1 and num2 : %d %d \n",num1,num2);
    swap(num1,num2);
    printf("After swap num1 and num2 : %d %d \n",num1,num2);
}

The output of the program is:

Before swap num1 and num2 : 4 9 
After swap num1 and num2 : 4 9

The code to swap two variables do not work as intended because the variables x and y are different to num1 and num2; they are stored at different addresses, and are simply initialised with the values of num1 and num2.

The desired effect of this function can be achieved by using pointers. Pointers, as with any other variable, are passed by value, but their values are addresses which, when copied, still point to the original variables.

Here is modified program to swap a numbers using pointers.

#include<stdio.h>

void swap(int* px, int* py)     /* px and py are copies of the passed pointer arguments. */
{
    int tmp = *px;              /* The value of px is still the address of num1 */
    *px = *py;                  /* so this dereferencing operation is equivalent to a = b. */
    *py = tmp;
}

int main()
{
    int num1 = 4, num2 =9;
    printf("Before swap num1 and num2 : %d %d \n",num1,num2);
    swap(&num1, &num2);               /* Pass pointers to a and b, respectively. */
    printf("After swap num1 and num2 : %d %d \n",num1,num2);
}

The ouput of the program is:

Before swap num1 and num2 : 4 9
After swap num1 and num2 : 4 9

The way that swap took place above can be conceptualized pictorically as,

pointer-pass-by-ref

Pointers provide indirect access to variables. This is why the * operator is called the indirection operator. Passing pointers as function arguments, therefore, is known as “pass-by-reference”.

If we need to return multiple values like in swap(), the pass by reference semantics becomes useful. It is also useful as a mechanism to avoid copying large objects between functions; rather than make a copy of a large object, it is sufficient to pass a pointer to the object.

Reference : The C programming language

C Arrays
C Structures