Pointers in C
Pointers in C
Understanding Pointers in C
Pointer is a variable just like other variables of c but only
difference is unlike the other variable it stores the memory
address of any other variables of c. This variable may be type
of int, char, array, structure, function or any other pointers. For
examples:
(1) Pointer p which is storing memory address of an int type
variable:
int i=50;
This statement instructs the system to find a location for the integer variable i and puts the value
of 50 in that location
i Variable
50 Value
5001 Address
int *p=&i;
Since a pointer is a variable, its value is also stored in the memory in another location.
Variable Value Address
i 50 5001
p 5001 5048
Since the value of variable p is the address of the variable
i, we may access the value of i by using the value of p and
therefore, we say that the variable p points to the variable i.
(2) Pointer p which is storing memory address of an array:
int arr[20];
int (*p)[20]=&arr;
(3) Pointer p which is storing memory address of a function:
char display(void);
char(*p)(void)=&display;
(4) Pointer p which is storing memory address of struct type
variable:
struct abc{
1
int a;
float b;
}var;
struct abc *p=&var;
Note: A variable where it will be stored in memory is decided by
operating system. We cannot guess at which location a particular
variable will be stored in memory.
Note:
* is known as indirection operator which gives content of any
variable
& is known as reference operator which gives address where variable
has stored in memory.
Example
p = &qty;
It gives the address of the variable qty. so the output will be
5000.
2
If x is an array then,
&x[0] and &x[i+3] are valid.
Example program:
main()
{
Char a=’A’;
int x=125;
float p=10.25, q=18.76;
printf(“%c is stored at addr %u \n”,a, &a);
printf(“%d is stored at addr %u \n”,x, &x);
printf(“%f is stored at addr %u \n”,p, &p);
printf(“%f is stored at addr %u \n”,q, &q);
}
Output
A is stored at addr 4436.
125 is stored at addr 4434.
10.250000 is stored at addr 4442.
18.760000 is stored at addr 4438.
Initialization:
The process of assigning the address of a variable to a pointer
variable is known as initialization.
Once the pointer variable is declared we can use the assignment
operator to initialize the variable.
3
Example:
int quantity;
int *p; /*declaration*/
p=&quantity; /*initialization*/
4
Chain of pointers:
It is possible to make a pointer to point to another pointer, thus
creating a chain of pointers s shown:
p2 p1 variable
Pointer Expressions
Like other variables, pointer variables can be used in
expressions.
Examples
y = *p * *p1; same as (*p) * (*p1)
sum=sum+ *p1;
*p2 = *p2 + 10;
We may also use short-hand operators with the pointers.
Examples
p1++;
Sum += p2;
Pointers can also be used to compared using relational operators.
Examples
p1 > p2;
p1 = = p2;
5
We may not use pointers in multiplication or division. . Similarly,
two pointers cannot be added.
Examples
p1 / p2;
p1 + p2;
p1 * p2;
are not allowed.
Value 1 2 3 4 5
Base Address
The name x is defined as a constant pointer to the first elememt,
x[0] and the value of x is 1000, the location where x[0] is
stored.
x=&x[0]=1000
6
If we declare p as integer pointer, then we can make the pointer
p to the array x by the following assignment:
p=x;
This is equivalent to
p=&x[0];
Now we can access every value of x using p++ to move from one
element to another. The relationship between p and x is shown as:
p=&x[0] (=1000)
p+1=&x[0] (=1002)
p+2=&x[0] (=1004)
p+3=&x[0] (=1006)
p+4=&x[0] (=1008)
The address of an element is calculated using its index and the
scale factor of the data type, for instance
Address of x[3]= base address + (3 * scale factor of int)
= 1000 +(3 *2) = 1006
The pointer accessing method is much faster than array indexing.
7
D – is stored at address 54
E – is stored at address 55
L – is stored at address 56
H – is stored at address 57
I – is stored at address 58
Array of Pointers:
One important use of pointers is in handling of a table of strings.
Consider the following array of strings:
char name[3][25];
This says that the name is a table of strings containing three
names, each with a maximum length of 25 characters. The total
storage requirements for the name are 75 bytes.
Therefore instead of making each row fixed number of characters, we
can make it a pointer to a string of varying length. For ex.
char *name [3] = {
“New Zealand”,
“Canada”,
“India”
};
Declares name to be an array of three pointers to characters, each
pointer pointing to a particular name as:
name[0] New Zealand
name[1] Canada
name[3] India
This declaration allocates only 28 bytes, sufficient to hold the
characters as shown below:
for(i=0;i<=2;i++)
printf(“%s\n”,name[i]); /Statement would print all the three names
The character arrays with the varying length are called “ragged
arrays”.
8
}
Some important points are as follows:
1. The function parameters are declared as pointers.
2. The dereferenced pointers are used in the function body.
3. When the function is called, the addresses are passed as
actual arguments
Example: Function using pointers to exchange the values stored in
two locations in the memory.
Program:
#include<stdio.h>
void exchange(int *, int *)
main()
{
int x=100, y=200;
printf(“Before exchange: x=%d y=%d”, x, y);
exchange(&x, &y); /*function call*/
printf(“After exchange: x=%d y=%d”, x, y);
}
exchange(int *a, int *b)
{
int t;
t=*a ; /* assign the value at address a to t */
*a=*b; /* put b into a */
*b=t; /* put t into b */
}
Output:
Before exchange: x=100 y=200
After exchange: x=200 y=100
9
then assigned to the pointer variable p in the calling function. In
this case, the address of the b is returned and assigned to p and
therefore the output will be the value of b, namely 20.
Pointers to Functions:
A function, like a variable, has an address location in the
memory.
It is also possible to declare a pointer to a function, which can
then be used as an argument in another function.
A pointer to a function is declared as follows:
type(*fptr)();
This tells the compiler that fptr is a pointer to a function
which returns type value. The parantheses around *fptr are
necessary. A statement like
type *gptr();
would declare gptr as a function returning a pointer to type.
We can make a function pointer to point to a specific function by
simply assigning the name of the function to the pointer.
Example
double mul(int , int) ;
double (*p1) ( ) ;
p1 = mul;
declare p1 as a pointer to a function and then make p1 to point to
the function mul. To call the function mul, we may now use the
pointer p1 with the list of parameters as shown below:
(*p1)(x,y) /* Function Call */
is equivalent to
mul(x,y) /* Both statements are same */
10
The following for statement will print the values of members of
all the elements of product array.
for(ptr = product ; ptr < product ; ptr++)
printf(“%s %d %f \n”, ptr name, ptrnumber, ptrprice);
We could also use the notation
(*ptr).number
to access the member number. The parenthesis around *ptr are
necessary because the member operator ‘.’ has a higher precedence
than the operator.
Example1:
#include <stdio.h>
#include <conio.h>
int main ()
{
struct st
{
int id;
char *name;
char *address;
};
struct st employee, *stptr;
stptr = &employee;
stptr->id = 1;
stptr->name = "Angelina";
stptr->address ="Rohini,Delhi";
printf("Employee Information:id=%d\n%s\n%s\n",stptr->id, stptr->
name,stptr->address);
getch();
return 0;
}
OUTPUT:
Employee Information:
id=1
name = "Angelina
address ="Rohini,Delhi
Example2:
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
void printBook( struct Books *book ); /* function declaration */
int main( )
{
struct Books Book1; /* Declare Book1 of type Book */
struct Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
11
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* book 2 specification */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* print Book1 info by passing address of Book1 */
printBook( &Book1 );
/* print Book2 info by passing address of Book2 */
printBook( &Book2 );
return 0;
}
void printBook( struct Books *book )
{
printf( "Book title : %s\n", book->title);
printf( "Book author : %s\n", book->author);
printf( "Book subject : %s\n", book->subject);
printf( "Book book_id : %d\n", book->book_id);
}
Output:
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
12
Bit Fields
This structure requires 8 bytes of memory space but in actual we are
going to store either 0 or 1 in each of the variables. The C
programming language offers a better way to utilize the memory space
in such situation. If you are using such variables inside a structure
then you can define the width of a variable which tells the C
compiler that you are going to use only those numbers of bytes. For
example, above structure can be re-written as follows:
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
Now, the above structure will require 4 bytes of memory space for
status variable but only 2 bits will be used to store the values. If
you will use up to 32 variables each one with a width of 1
bit , then also status structure will use 4 bytes, but as soon as you
will have 33 variables, then it will allocate next slot of the
memory and it will start using 64 bytes. Let us check the following
example to understand the concept:
#include <stdio.h>
#include <string.h>
/* define simple structure */
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* define a structure with bit fields */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( )
{
printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
OUTPUT:
Memory size occupied by status1: 8
Memory size occupied by status2: 4
13
Elements Description
type An integer type that determines how the bit-field's
value is interpreted. The type may be int, signed
int, unsigned int.
member_name The name of the bit-field.
width The number of bits in the bit-field. The width must
be less than or equal to the bit width of the
specified type.
The variables defined with a predefined width are called bit fields.
A bit field can hold more than a single bit for example if you need a
variable to store a value from 0 to 7 only then you can define a bit
field with a width of 3 bits as follows:
struct
{
unsigned int age : 3;
} Age;
The above structure definition instructs C compiler that age variable
is going to use only 3 bits to store the value; if you will try to
use more than 3 bits then it will not allow you to do so. Let us try
the following example:
#include <stdio.h>
#include <string.h>
struct
{
unsigned int age : 3;
} Age;
int main( )
{
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
Age.age = 8;
printf( "Age.age : %d\n", Age.age );
return 0;
}
When the above code is compiled it will compile with
warning and when executed, it
produces the following result:
Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0
14
THE PREPROCESSOR
INTRODUCTION:
The unique feature of the C language is the preprocessor. The C
preprocessor provides several tools that are unavailable in other high-
level languages. The programmer can use these tools to make program
easy to read, easy to modify, portable, and more efficient.
The preprocessor is a program that processes the source code
before it passes through the compiler. It operates under the control of
preprocessor command lines or directives. Preprocessor directives are
places in the source program before the main line.
Before the source code passes through the compiler, it is examined
by the preprocessor for any preprocessor directives. If there are any,
appropriate actions are taken and then the source program is handed
over to the compiler.
The preprocessor follows special syntax rules that are different
from the normal C syntax. They all begin with the symbol # in column
one and do not require a semicolon at the end.
A set of commonly used preprocessor directives and their functions
is given in below table:
Directive Function
#define Defines a macro substitution
#undef Undefines a macro
#include Specifies the files to be included
#ifdef Test for a macro definition
#endif Specifies the end of #if
#ifndef Test whether a macro is not
defined
#if Test a compile-time condition
#else Specifies alternatives when #if
test fails
15
2. File inclusion directives
3. Compiler control directives
16
MACRO SUBSTITUTION
Macros are operation defined as symbols. Macro substitution is a
process where an identifier in a program is replaced by a predefined
string composed of one or more tokens. The preprocessor accomplishes
this task under the direction of #define statement. This statement,
usually known as a macro definition takes the following general form:
#define identifier string
This statement is included at the beginning of the program, and
then the preprocessor replaces every occurrence of the identifier in the
source code by the string. The definition is not terminated by semicolon.
The string may be any text, while the identifier must be valid C name.
There are different forms of macro substitution. The most common
forms are:
1. Simple macro substitution
2. Argumented macro substitution
3. Nested macro substitution
17
printf(“M=%d\n”, M);
these two line would be changed during preprocessing as follows:
total=5*value;
printf(“M=%d\n”, 5); /“M=%d\n” is left unchanged
A macro definition can include more than a simple constant value. It can
include expressions as well. Following are valid definitions:
#define AREA 5*12.46
#define SIZE sizeof(int)*4
#define TWO-PI 2.0*3.14
Whenever we use expression is used care should be taken to prevent an
unexpected order of evaluation. Consider the evaluation of the equation,
ratio=D/A;
where D and A are macros defined as follows:
#define D 45-22
#define A 78+32
The result of the preprocessor’s substitution for D and A is:
ratio = 45-22/78+32
this is certainly different from the expected expression
(45-22) / (78+32)
Correct results can be obtained from by using parenthesis around the
strings as:
#define D (45-22)
#define A (78+32)
18
volume = (side * side * side);
Consider the following statement:
volume = CUBE (a + b);
This would expand to:
volume = (a + b * a + b * a + b);
Obviously the above statement not produce correct results, corrected
using parenthesis for each occurrence of a formal argument in the
string.
Nesting of macros:
We can also use one macro in the definition of another macro. That is
the macro definition can be nested.
Ex:
#define M 5
#define N M+1
#define SQUARE(x) ((x) *(x))
#define CUBE(x) (SQUARE(x) *(x))
#define SIXTH(x) (CUBE(x) * CUBE(x))
Macros can also be used as parameters of other macros. Ex:
#define MAX(M,N) (( (M) >(N)) ? (M) : (N)
FILE INCLUSION
An external file containing functions or macro definitions can be included
as a part of a program so that we need not rewrite those functions or
macro definitions. This is achieved by the #include preprocessor
directive. The two forms of the #include directive are:
#include “filename”
#include <filename>
where filename is the name of the file containing the required definitions
or functions. The preprocessor inserts the entire contents of filename
into source code of the program. When the filename is included within
19
the double quotation marks, the search for the file is made first in the
current directory and then in standard directories.
If the included file is not found, an error is reported and compilation is
terminated.
Ex:
#include <stdio.h>
#include “stdlib.h”
20
COMPILER CONTROL DIRECTIVES:
In C, there are several preprocessor directives that allow us to
selectively compile portions of the program’s source code. This process
is known as conditional compilation. This enables the programmers to
control the execution of preprocessor directives and compilation of
program code. Each of the preprocessor directives evaluates a constant
integer expression.
Conditional compilation is performed using the following preprocessor
directives:
#if
#else
#elif
#endif
The conditional preprocessor directives allow us to conditionally include
the portions of the statements based upon the outcome of a constant
expression.
Syntax:
#if constant-expression
statement sequence
#endif
If the conditional expression is true, the statement sequence that is
placed between #if and #endif is compiled. Otherwise the statement
sequence is skipped.
Ex:
#define COUNT 50
main()
{
#if count>20
printf(“The count is greater than 20! \n”) ;
#endif
}
21
22