C langugae : Pointers

A pointer is a reference to a data object or a function. Pointers have many uses: defining "call-by-reference" functions, and implementing dynamic data structures such as chained lists and trees.
For example, if you need to sort a large number of large records, it is often more efficient to sort a list of pointers to the records, rather than moving the records themselves around in memory. Similarly, if you need to pass a large record to a function, it's more economical to pass a pointer to the record than to pass the record contents, even if the function doesn't modify the contents.
There are two pointer operators in C:
& The "address of" operator
* The dereferencing operator
1. & The "address of" operator : - The & operator always produces the memory address of whatever it precedes. The * operator, when used with pointers, either declares a pointer or dereferences the pointer's value.
2. * The dereferencing operator : - * means, of course, multiplication. The asterisk is called an overloaded operator. Overloaded operators perform more than one kind of operation depending on how you use them in your programs. C does not confuse * for multiplication when you use it as a dereferencing operator with pointers.
Declaring Pointers
A pointer represents both the address and the type of an object or function. If an object or function has the type T, then a pointer to it has the derived type pointer to T. For example, if var is a float variable, then the expression &varwhose value is the address of the float variablehas the type pointer to float, or in C notation, the type float *. A pointer to any type T is also called a T pointer for short. Thus the address operator in &var yields a float syntax:
type * [type-qualifier-list] name [= initializer];
Ex: -
int *iPtr;     // Declare iPtr as a pointer to int.
pt = &ar1[0][0]; // both pointer-to-int
pt = ar1[0]; // both pointer-to-int
pt = ar1; // not valid
pa = ar1; // both pointer-to-int[3]
pa = ar2; // not valid
p2 = &pt; // both pointer-to-int *
*p2 = ar2[0]; // both pointer-to-int
p2 = ar2; // not valid
Null Pointers
A null pointer is what results when you convert a null pointer constant to a pointer type. A null pointer constant is an integer constant expression with the value 0, or such an expression cast as the type void *. The macro NULL is defined in stdlib.h, stdio.h and other header files as a null pointer constant.
A null pointer is always unequal to any valid pointer to an object or function. For this reason, functions that return a pointer type usually use a null pointer to indicate a failure condition. One example is the standard function fopen( ), which returns a null pointer if it fails to open a file in the specified mode:
#include
/* ... */
FILE *fp = fopen( "demo.txt", "r" );
if ( fp == NULL )
{
// Error: unable to open the file demo.txt for reading.
}
void Pointers
A pointer to void, or void pointer for short, is a pointer with the type void *. As there are no objects with the type void, the type void * is used as the all-purpose pointer type. In other words, a void pointer can represent the address of any objectbut not its type. To access an object in memory, you must always convert a void pointer into an appropriate object pointer.
To declare a function that can be called with different types of pointer arguments, you can declare the appropriate parameters as pointers to void. When you call such a function, the compiler implicitly converts an object pointer argument into a void pointer. A common example is the standard function memset( ), which is declared in the header file string.h with the following prototype:
void *memset( void *s, int c, size_t n );
The function memset( ) assigns the value of c to each of the n bytes of memory in the block beginning at the address s. For example, the following function call assigns the value 0 to each byte in the structure variable record:
Initializing Pointers
Pointer variables with automatic storage duration start with an undefined value, unless their declaration contains an explicit initializer. All variables defined within any block, without the storage class specifier static, have automatic storage duration. All other pointers defined without an initializer have the initial value of a null pointer.
You can initialize a pointer with the following kinds of initializers:
1. A null pointer constant.
2. A pointer to the same type, or to a less qualified version of the same type
3. A void pointer, if the pointer being initialized is not a function pointer. Here again, the pointer being initialized can be a pointer to a more qualified type.
Pointers that do not have automatic storage duration must be initialized with a constant expression, such as the result of an address operation or the name of an array or function.
When you initialize a pointer, no implicit type conversion takes place except in the cases just listed. However, you can explicitly convert a pointer value to another pointer type. For example, to read any object byte by byte, you can convert its address into a char pointer to the first byte of the object:
double x = 1.5;
char *cPtr = &x;    // Error: type mismatch; no implicit conversion.
char *cPtr = (char *)&x;      // OK: cPtr points to the first byte of x.
Pointers and Type Qualifiers
The declaration of a pointer may contain the type qualifiers const, volatile, and/or restrict. The type qualifiers const and volatile may qualify either the pointer type itself, or the type of object it points to.
short const volatile * restrict ptr;
In this declaration, the keyword restrict qualifies the pointer ptr. This pointer can refer to objects of type short that may be qualified with const or volatile, or both.
Constant Pointers and Pointers to Constant Objects
int *ptr = &var;    &var // An int pointer that points to var.
*ptr = 77;     &var // OK: ptr is not a read-only pointer.
ptr_to_const = ptr;     &var // OK: implicitly converts ptr from "pointer to int"
    &var;     // into "pointer to const int".
*ptr_to_const = 77;     &var // Error: can't modify a variable through a read-only
    &var;  // pointer.
ptr = &c_var;     &var;  // Error: can't implicitly convert "pointer to const
    &var;  // int" into "pointer to int".
ptr = (int *)&c_var;     &var // OK: Explicit pointer conversions are always
    &var;  // possible.
*ptr = 200;
    &var;  // Attempt to modify c_var: possible runtime error.
int i = 3 ;
This declaration tells the C compiler to:
(a) Reserve space in memory to hold the integer value.
(b) Associate the name i with this memory location.
(c) Store the value 3 at this location.

We can print this address number through the following program:
main( )
{
int i = 3 ;
printf ( "\nAddress of i = %u", &i ) ;
printf ( "\nValue of i = %d", i ) ;
printf ( "\nValue of i = %d", *( &i ) ) ;
}
The output of the above program would be:
Address of i = 65524
Value of i = 3
Value of i = 3
Note that printing the value of *( &i ) is same as printing the value of i.
j = &i ; But remember that j is not an ordinary variable like any other integer variable. It is a variable that contains the address of other variable (i in this case). Since j is a variable the compiler must provide it space in the memory. Once again, the following memory map would illustrate the contents of i and j.

int *j ;
This declaration tells the compiler that j will be used to store the address of an integer value. In other words j points to an integer.
Here is a program that demonstrates the relationships we have been discussing.
main( )
{
int i = 3, *j, **k ;
j = &i ;
k = &j ;
printf ( "\nAddress of i = %u", &i ) ;
printf ( "\nAddress of i = %u", j ) ;
printf ( "\nAddress of i = %u", *k ) ;
printf ( "\nAddress of j = %u", &j ) ;
printf ( "\nAddress of j = %u", k ) ;
printf ( "\nAddress of k = %u", &k ) ;
printf ( "\nValue of j = %u", j ) ;
printf ( "\nValue of k = %u", k ) ;
printf ( "\nValue of i = %d", i ) ;
printf ( "\nValue of i = %d", * ( &i ) ) ;
printf ( "\nValue of i = %d", *j ) ;
printf ( "\nValue of i = %d", **k ) ;
}
The output of the above program would be:
Address of i = 65524
Address of i = 65524
Address of i = 65524
Address of j = 65522
Address of j = 65522
Address of k = 65520
Value of j = 65524
Value of k = 65522
Value of i = 3
Value of i = 3
Value of i = 3
Value of i = 3

Pointers to Arrays and Arrays of Pointers
Pointers occur in many C programs as references to arrays , and also as elements of arrays. A pointer to an array type is called an array pointer for short, and an array whose elements are pointers is called a pointer array.
int (* arrPtr)[10] = NULL; // A pointer to an array of
                   // ten elements with type int.
int matrix[3][10]; // Array of three rows, each with 10 columns.
// The array name is a pointer to the first
// element; i.e., the first row.
arrPtr = matrix; // Let arrPtr point to the first row of
// the matrix.
(*arrPtr)[0] = 5; // Assign the value 5 to the first element of the
// first row.
//
arrPtr[2][9] = 6; // Assign the value 6 to the last element of the
// last row.
//
++arrPtr; // Advance the pointer to the next row.
(*arrPtr)[0] = 7; // Assign the value 7 to the first element of the
// second row.

Arguments can generally be passed to functions in one of the two ways:
1. Call By Value
2. Call By Reference
1. Call By Value
main( )
{
int a = 10, b = 20 ;
swapv ( a, b ) ;
printf ( "\na = %d b = %d", a, b ) ;
}
swapv ( int x, int y )
{
int t ;
t = x ;
x = y ;
y = t ;
printf ( "\nx = %d y = %d", x, y ) ;
}
The output of the above program would be:
x = 20 y = 10
a = 10 b = 20
Note that values of a and b remain unchanged even after exchanging the values of x and y.
In the second method (call by reference) the addresses of actual arguments in the calling function are copied into formal arguments of the called function.
main( )
{
int a = 10, b = 20 ;
swapr ( &a, &b ) ;
printf ( "\na = %d b = %d", a, b ) ;
}
swapr( int *x, int *y )
{
int t ;
t = *x ;
*x = *y ;
*y = t ;
}
The output of the above program would be:
a = 20 b = 10
Note that this program manages to exchange the values of a and b using their addresses stored in x and y. Conclusions
1. If we want that the value of an actual argument should not get changed in the function being called, pass the actual argument by value.
2. If we want that the value of an actual argument should get changed in the function being called, pass the actual argument by reference.
3. If a function is to be made to return more than one value at a time then return these values indirectly by using a call by reference.
Pointer Advantages
An array name is a pointer constant, not a pointer variable. You cannot change the value of an array name, because you cannot change constants. This explains why you cannot assign an array new values during a program's execution. For instance, even if cname is a character array, the following is not valid in C:
cname = "Christine Chambers"; /* Invalid array assignment */
The array name, cname, cannot be changed because it is a constant. You would not attempt the statement
5 = 4 + 8 * 21; // Invalid assignment
because you cannot change the constant 5 to any other value. C knows that you cannot assign anything to 5, and C will print an error message if you attempt to change 5. C also knows that an array name is a constant and that you cannot change an array to another value. (You can assign values to an array only at declaration time, one element at a time during execution, or by using functions such as strcpy().
You can change a pointer variable, and being able to do so makes processing virtually any data, including arrays, much more powerful and flexible.
Example: - /*Changes the value of a pointer variable */
#include < stdio.h >
main( )
{
float v1=676.54; // Defines two
float v2=900.18; // floating-point variables
float * pV; // Defines a floating-point pointer
pV = &v1; // Makes pointer point to v1
printf(''The first value is %.2f \n", *pV); // Prints
// 676.54
pV = &v2; // Changes the pointer so that it
// points to v2
printf("The second value is %.2f \n", *pV); // Prints
// 900.18
return 0;
}
Restricted Pointers
C99 introduced the type qualifier restrict , which is applicable only to object pointers. A pointer qualified with restrict is called a restricted pointer. There is a special relationship between a restrict-qualified pointer and the object it points to: during the lifetime of the pointer, either the object is not modified, or the object is not accessed except through the restrict-qualified pointer. An example:
typedef struct { long key; // Define a structure type.
/* ... other members ... */
} Data_t;
Data_t * restrict rPtr = malloc( sizeof(Data_t) ); // Allocate a structure
This example illustrates one way to respect the relationship between the restricted pointer and its object: the return value of malloc( )the address of an anonymous Data_t objectis assigned only to the pointer rPtr, so the program won't access the object in any other way.
The restrict type qualifier is a hint to the compiler that allows it to apply certain optimization techniques that might otherwise introduce inconsistencies. However, the restrict qualifier does not mandate any such optimization, and the compiler may ignore it. The program's outward behavior is the same in either case.
The function scalar_product( )
// This function calculates the scalar product of two arrays.
// Arguments: Two arrays of double, and their length.
// The two arrays need not be distinct.
double scalar_product( const double * restrict p1,
const double * restrict p2,
int n )
{
double result = 0.0;
for ( int i = 0; i < n; ++i )
result += p1[i] * p2[i];
return result;
}
Assuming an array named P with three double elements, you could call this function using the expression scalar_products( P, P, 3 ). The function accesses objects through two different restricted pointers, but as the const keyword in the first two parameter declarations indicates, it doesn't modify them.


Free Web Hosting