POINTERS:
The correct understanding and use of pointers is crucial to successful C programming.
There are several reasons for this:
First, pointers provide the means by which functions can modify their calling arguments.
Third, pointers can improve the efficiency of certain routines, and
Finally, pointers provide support for dynamic data structures, such as binary trees and linked lists.
Pointers are one of the strongest but also one of the most dangerous features in C.
For example, a pointer containing an invalid value can cause your program to crash.
Define a Pointer
A pointer is a variable that holds a memory address. This address is the location of another object (typically another variable) in memory.
For example, if one variable contains the address of another variable, the first variable is said to point to the second. Figure illustrates this situation.
One variable points to another
Pointer Variable Declaration
A pointer declaration consists of a base type, an *, and the variable name.
The general form for declaring a pointer variable is
where ,
· type is the base type of the pointer and may be any valid type.
· The name of the pointer variable is specified by name.
· The base type of the pointer defines the type of object to which the pointer will point.
· Technically, any type of pointer can point anywhere in memory.
· All pointer operations are done relative to the pointer's base type.
For example, when you declare a pointer to be of type int *, the compiler assumes that any address that it holds points to an integer— whether it actually does or not. (That is, an int * pointer always ''thinks" that it points to an int object, no matter what that piece of memory actually contains.)
Therefore, when you declare a pointer, you must make sure that its type is compatible with the type of object to which you want to point.
Pointer Variable Initialization
Pointer Initialization is the process of assigning address of a variable to pointer variable.
Pointer variable contains address of variable of same data type.
In C language address operator & is used to determine the address of a variable.
The & (immediately preceding a variable name) returns the address of the variable associated with it.
int a = 10 ;
int *ptr ; //pointer declaration
ptr = &a ; //pointer initialization
or,
int *ptr = &a ; //initialization and declaration together
Pointer variable always points to same type of data.
float a;
int *ptr;
ptr = &a; //ERROR, type mismatch
Example will clearly explain the initialization of Pointer Variable.
#include<stdio.h>
int main()
{
int a; // Step 1
int *ptr; // Step 2
a = 10; // Step 3
ptr = &a; // Step 4
return(0);
}
Explanation of Above Program :
· Pointer should not be used before initialization.
· “ptr” is pointer variable used to store the address of the variable.
· Stores address of the variable ‘a’ .
· Now “ptr” will contain the address of the variable “a” .
Note :
Pointers are always initialized before using it in the program
Example : Initializing Integer Pointer
#include<stdio.h>
int main()
{
int a = 10;
int *ptr;
ptr = &a;
printf("\nValue of ptr : %u",ptr);
return(0);
}
Output :
Value of ptr : 4001
The Pointer Operators
There are two pointer operators:
* and &.
Ø The & is a unary operator that returns the memory address of its operand. (A unary operator only requires one operand.)
For example,
m = &count; &-- “ address of”
places into m the memory address of the variable count.
o This address is the computer's internal location of the variable. It has nothing to do with the value of count .
o Therefore, the preceding assignment statement can be verbalized as "m receives the address of count ."
o Assume that the variable count uses memory location 2000 to store its value and count has a value of 100.
o Then, after the preceding assignment, m will have the value 2000.
Ø The second pointer operator, *, is the complement of &.
· It is a unary operator that returns the value located at the address that follows.
For example, if m contains the memory address of the variable count,
q = *m; * -- "at address."
· places the value of count into q. Thus, q will have the value 100 because 100 is stored at location 2000, which is the memory address that was stored in m.
· In this case, the preceding statement can be verbalized as "q receives the value at address m."
Pointer Expressions
In general, expressions involving pointers conform to the same rules as other expressions. A few special aspects of pointer expressions, such as assignments, conversions, and arithmetic.
Pointer Assignments
Ø You can use a pointer on the right-hand side of an assignment statement to assign its value to another pointer.
Ø When both pointers are the same type, the situation is straightforward.
For example:
#include <stdio.h>
int main(void)
{
int x = 99;
int *p1, *p2;
p1 = &x;
p2 = p1; /* print the value of x twice */
printf(''Values at p1 and p2: %d %d\n", *p1, *p2); /* print the address of x twice */
printf("Addresses pointed to by p1 and p2: %p %p", p1, p2);
return 0;
}
After the assignment sequence
p1 = &x; p2 = p1;
· p1 and p2 both point to x.
· Thus, both p1 and p2 refer to the same object.
· Sample output from the program, which confirms this, is shown here.
· Values at p1 and p2: 99 99
· Addresses pointed to by p1 and p2: 0063FDF0 0063FDF0
The addresses are displayed by using the %p printf( ) format specifier, which causes printf( ) to display an address in the format used by the host computer.
Pointer Conversions:
Ø One type of pointer can be converted into another type of pointer.
Ø There are two general categories of conversion: those that involve void * pointers, and
those that don't.
Ø In C, it is permissible to assign a void * pointer to any other type of pointer.
Ø It is also permissible to assign any other type of pointer to a void * pointer.
Ø A void * pointer is called a generic pointer.
Ø The void * pointer is used to specify a pointer whose base type is unknown.
Ø The void * type allows a function to specify a parameter that is capable of receiving any type of pointer argument without reporting a type mismatch.
Ø It is also used to refer to raw memory ,when the semantics of that memory are not known.
Ø No explicit cast is required to convert to or from a void * pointer.
Ø Except for void *, all other pointer conversions must be performed by using an explicit cast.
Ø The conversion of one type of pointer into another type may create undefined behavior.
Ø For example, consider the following program that attempts to assign the value of x to y, through the
pointer p.
This program compiles without error, but does not produce the desired result.
#include <stdio.h>
int main(void)
{
double x = 100.1, y;
int *p; /* The next statement causes p (which is an integer pointer) to point to a double. */
p = (int *) &x; /* The next statement does not operate as expected. */
y = *p; /* attempt to assign y the value x through p */
printf(''The (incorrect) value of x is: %f", y); /* statement won't output 100.1. */
return 0;
}
· An explicit cast is used when assigning the address of x (which is implicitly a double * pointer) to p, which is an int * pointer.
· While this cast is correct, it does not cause the program to act as intended.
· To understand the problem, assume 4-byte ints and 8-byte doubles.
· Because p is declared as an integer pointer, only 4 bytes of information will be transferred to y by this assignment statement, y = *p; not the 8 bytes that make up a double.
· Thus, even though p is a valid pointer, the fact that it points to a double does not change the fact that operations on it expect intvalues.
· Thus, the use to which p is put is invalid.
· Thus, pointer operations are governed by the type of the pointer, not the type of the object being pointed to.
· One other pointer conversion is allowed: You can convert an integer into a pointer or a pointer into an integer.
· We must use an explicit cast, and the result of such a conversion is implementation defined and may result in undefined behavior. (A cast is not needed when converting zero, which is the null pointer.)
Pointer Arithmetic
There are only two arithmetic operations that you can use on pointers:
addition and subtraction.
To understand what occurs in pointer arithmetic, let p1 be an integer pointer with a current value of 2000.
Also, assume ints are 2 bytes long.
After the expression
p1++;
p1 contains 2002, not 2001.
The reason for this is that each time p1 is incremented, it will point to the next integer. The same is true of decrements. For example, assuming that p1 has the value 2000, the expression p1--; causes p1 to have the value 1998.
Generalizing from the preceding example, the following rules govern pointer arithmetic.
· Each time a pointer is incremented, it points to the memory location of the next element of its base type.
· Each time it is decremented, it points to the location of the previous element.
· When applied to char pointers, this will appear as ''normal" arithmetic because a char object is always 1 byte long no matter what the environment.
· All other pointers will increase or decrease by the length of the data type they point to.
· This approach ensures that a pointer is always pointing to an appropriate element of its base type.
Below figure illustrates this concept.
We may add or subtract integers to or from pointers.
The expression p1 = p1 + 12; makes p1 point to the 12th element of p1's type beyond the one it currently points to.
Besides addition and subtraction of a pointer and an integer, only one other arithmetic operation is allowed:
You can subtract one pointer from another in order to find the number of objects of their base type that separate the two.
All other arithmetic operations are prohibited, such as
· We cannot multiply or divide pointers;
· We cannot add two pointers;
· We cannot apply the bitwise operators to them; and
· We cannot add or subtract type float or double to or from pointers.