You are on page 1of 30

Unit 5

C Pointers

In this tutorial, you'll learn about pointers; what pointers are, how do you use them and the
common mistakes you might face when working with them with the help of examples.

Pointers are powerful features of C and C++ programming. Before we learn pointers, let's
learn about addresses in C programming.

Address in C

If you have a variable var in your program, &var will give you its address in the memory.
We have used address numerous times while using the scanf() function.

scanf("%d", &var);

Here, the value entered by the user is stored in the address of var variable. Let's take a
working example.

#include <stdio.h>
int main()
{
int var = 5;
printf("var: %d\n", var);

// Notice the use of & before var


printf("address of var: %p", &var);
return 0;
}
Output

var: 5
address of var: 2686778

Note: You will probably get a different address when you run the above code.

C Pointers

Pointers (pointer variables) are special variables that are used to store addresses rather than
values.

Pointer Syntax

Here is how we can declare pointers.

int* p;

Here, we have declared a pointer p of int type.


You can also declare pointers in these ways.

int *p1;
int * p2;

Let's take another example of declaring pointers.


int* p1, p2;

Here, we have declared a pointer p1 and a normal variable p2.

Assigning addresses to Pointers

Let's take an example.

int* pc, c;
c = 5;
pc = &c;

Here, 5 is assigned to the c variable. And, the address of c is assigned to the pc pointer.

Get Value of Thing Pointed by Pointers

To get the value of the thing pointed by the pointers, we use the * operator. For example:

int* pc, c;
c = 5;
pc = &c;
printf("%d", *pc); // Output: 5

Here, the address of c is assigned to the pc pointer. To get the value stored in that address,
we used *pc.
Note: In the above example, pc is a pointer, not *pc. You cannot and should not do
something like *pc = &c;
By the way, * is called the dereference operator (when working with pointers). It operates on
a pointer and gives the value stored in that pointer.

Changing Value Pointed by Pointers

Let's take an example.

int* pc, c;
c = 5;
pc = &c;
c = 1;
printf("%d", c); // Output: 1
printf("%d", *pc); // Ouptut: 1

We have assigned the address of c to the pc pointer.


Then, we changed the value of c to 1. Since pc and the address of c is the same, *pc gives us
1.
Let's take another example.

int* pc, c;
c = 5;
pc = &c;
*pc = 1;
printf("%d", *pc); // Ouptut: 1
printf("%d", c); // Output: 1

We have assigned the address of c to the pc pointer.


Then, we changed *pc to 1 using *pc = 1;. Since pc and the address of c is the same, c will
be equal to 1.
Let's take one more example.

int* pc, c, d;
c = 5;
d = -15;

pc = &c; printf("%d", *pc); // Output: 5


pc = &d; printf("%d", *pc); // Ouptut: -15

Initially, the address of c is assigned to the pc pointer using pc = &c;. Since c is 5, *pc gives
us 5.
Then, the address of d is assigned to the pc pointer using pc = &d;. Since d is -15, *pc gives
us -15.

Example: Working of Pointers

Let's take a working example.

#include <stdio.h>
int main()
{
int* pc, c;

c = 22;
printf("Address of c: %p\n", &c);
printf("Value of c: %d\n\n", c); // 22
pc = &c;
printf("Address of pointer pc: %p\n", pc);
printf("Content of pointer pc: %d\n\n", *pc); // 22

c = 11;
printf("Address of pointer pc: %p\n", pc);
printf("Content of pointer pc: %d\n\n", *pc); // 11

*pc = 2;
printf("Address of c: %p\n", &c);
printf("Value of c: %d\n\n", c); // 2
return 0;
}

Output

Address of c: 2686784
Value of c: 22

Address of pointer pc: 2686784


Content of pointer pc: 22

Address of pointer pc: 2686784


Content of pointer pc: 11

Address of c: 2686784
Value of c: 2
Explanation of the program
1. int* pc, c;

Here, a pointer pc and a normal variable c, both of type int, is created.


Since pc and c are not initialized at initially, pointer pc points to either no address or a
random address. And, variable c has an address but contains random garbage value.

2. c = 22;

This assigns 22 to the variable c. That is, 22 is stored in the memory location of variable c.

3. pc = &c;

This assigns the address of variable c to the pointer pc.

4. c = 11;
This assigns 11 to variable c.

5. *pc = 2;

This change the value at the memory location pointed by the pointer pc to 2.

Common mistakes when working with pointers

Suppose, you want pointer pc to point to the address of c. Then,

int c, *pc;

// pc is address but c is not


pc = c; // Error

// &c is address but *pc is not


*pc = &c; // Error

// both &c and pc are addresses


pc = &c; // Not an error

// both c and *pc are values


*pc = c; // Not an error

Here's an example of pointer syntax beginners often find confusing.


#include <stdio.h>
int main() {
int c = 5;
int *p = &c;

printf("%d", *p); // 5
return 0;
}

Why didn't we get an error when using int *p = &c;?


It's because

int *p = &c;

is equivalent to

int *p:
p = &c;

In both cases, we are creating a pointer p (not *p) and assigning &c to it.
To avoid this confusion, we can use the statement like this:

int* p = &c;

Now you know what pointers are, you will learn how pointers are related to arrays in the next
tutorial.
So far the operations using the C program are done on a prompt/terminal which is not stored
anywhere. But in the software industry, most programs are written to store the information
fetched from the program. One such way is to store the fetched information in a file. Different
operations that can be performed on a file are:

1. Creation of a new file (fopen() with attributes as “a” or “a+” or “w” or “w+”)
2. Opening an existing file (fopen())
3. Reading from file (fscanf() or fgets())
4. Writing to a file (fprintf() or fputs())
5. Moving to a specific location in a file (fseek(), rewind())
6. Closing a file (fclose())
The text in the brackets denotes the functions used for performing those operations.

Why do we need File Handling in C?


The output of a C program is generally deleted when the program is closed. Sometimes, we
need to store that output for purposes like data analysis, result presentation, comparison of
output for different conditions, etc. The use of file handling is exactly what the situation
calls for.

In order to understand why file handling makes programming easier, let us look at a few
reasons:

 Reusability: The file-handling process keeps track of the information created after the
program has been run.
 Portability: Without losing any data files can be transferred to another in the computer
system. The risk of flawed coding is minimized with this feature.
 Efficient: A large amount of input may be required for some programs. File handling
allows you to easily access a part of a code using individual commands which saves a
lot of time and reduces the chance of errors.
 Storage Capacity: Files allow you to store data without having to worry about storing
everything simultaneously in a program.
Types of Files in C
Generally, a text file contains alphabets, digits, and special characters or symbols, while a
binary file contains bytes or a compiled version of the text. It is important to recognize two
types of files when dealing with files:

 Text Files
 Binary Files
Text Files: Text files contain data in the form of ASCII characters and are generally used
to store a stream of characters. Each line in a text file ends with a new line character (‘/n’).
Text files are used to store the source code.
Binary Files: Binary files contain data that is stored in a similar manner to how it is stored
in the main memory. Instead of ASCII characters, it is stored in binary format. The binary
files can be created only from within a program and their contents can only be read by a
program.

Functions in File Operations:


Opening or Creating a File

For opening a file, fopen() function is used with the required access modes. Some of the
commonly used file access modes are mentioned below.

File opening modes in C:


Opening
Modes Description

Searches file. If the file is opened successfully fopen( ) loads it into memory
and sets up a pointer that points to the first character in it. If the file cannot
R be opened fopen( ) returns NULL.

Open for reading in binary mode. If the file does not exist, fopen( ) returns
Rb NULL.

Searches file. If the file exists, its contents are overwritten. If the file doesn’t
W exist, a new file is created. Returns NULL, if unable to open the file.

Open for writing in binary mode. If the file exists, its contents are
wb overwritten. If the file does not exist, it will be created.

Searches file. If the file is opened successfully fopen( ) loads it into memory
and sets up a pointer that points to the last character in it. If the file doesn’t
A exist, a new file is created. Returns NULL, if unable to open the file.

Open for append in binary mode. Data is added to the end of the file. If the
Ab file does not exist, it will be created.

Searches file. It is opened successfully fopen( ) loads it into memory and sets
up a pointer that points to the first character in it. Returns NULL, if unable to
r+ open the file.

Open for both reading and writing in binary mode. If the file does not exist,
rb+ fopen( ) returns NULL.
Opening
Modes Description

Searches file. If the file exists, its contents are overwritten. If the file doesn’t
w+ exist a new file is created. Returns NULL, if unable to open the file.

Open for both reading and writing in binary mode. If the file exists, its
wb+ contents are overwritten. If the file does not exist, it will be created.

Searches file. If the file is opened successfully fopen( ) loads it into memory
and sets up a pointer that points to the last character in it. If the file doesn’t
a+ exist, a new file is created. Returns NULL, if unable to open the file.

Open for both reading and appending in binary mode. If the file does not
ab+ exist, it will be created.

As given above, if you want to perform operations on a binary file, then you have to append
‘b’ at the last. For example, instead of “w”, you have to use “wb”, instead of “a+” you have
to use “a+b”. For performing the operations on the file, a special pointer called File pointer
is used which is declared as:

FILE *filePointer;

So, the file can be opened as

filePointer = fopen(“fileName.txt”, “w”)

The second parameter can be changed to contain all the attributes listed in the above table.

Reading From a File


The file read operations can be performed using functions fscanf or fgets. Both the functions
performed the same operations as that of scanf and gets but with an additional parameter, the
file pointer. So, it depends on you if you want to read the file line by line or character by
character.
And the code snippet for reading a file is as:

FILE * filePointer;

filePointer = fopen(“fileName.txt”, “r”);

fscanf(filePointer, "%s %s %s %d", str1, str2, str3, &year);

Writing to a File
The file write operations can be performed by the functions fprintf and fputs with similarities
to read operations. The snippet for writing to a file is as:

FILE *filePointer ;

filePointer = fopen(“fileName.txt”, “w”);

fprintf(filePointer, "%s %s %s %d", "We", "are", "in", 2012);

Closing a File
After every successful file operation, you must always close a file. For closing a file, you
have to use fclose() function. The snippet for closing a file is given as:

FILE *filePointer ;

filePointer= fopen(“fileName.txt”, “w”);


---------- Some file Operations -------

fclose(filePointer)

Functions for File Handling in C

Functions Description

fopen() to create a file or to open a file.

fclose() to close a file.

fgets() to read a file.

fprintf() to write blocks of data into a file.

fscanf() to read blocks of data from a file.

getc() to read a single character to a file.

putc() to write a single character to a file.

fseek() to set the position of a file pointer to a mentioned location.

ftell() to return the current position of a file pointer.

rewind() to set the file pointer to the beginning of a file.


Functions Description

putw() to write an integer to a file.

getw() to read an integer from a file.

The C Preprocessor is not a part of the compiler, but is a separate step in the compilation
process. In simple terms, a C Preprocessor is just a text substitution tool and it instructs the
compiler to do required pre-processing before the actual compilation. We'll refer to the C
Preprocessor as CPP.

All preprocessor commands begin with a hash symbol (#). It must be the first nonblank
character, and for readability, a preprocessor directive should begin in the first column. The
following section lists down all the important preprocessor directives −

Sr.No. Directive & Description

1
#define

Substitutes a preprocessor macro.

2
#include

Inserts a particular header from another file.

3
#undef

Undefines a preprocessor macro.

4
#ifdef

Returns true if this macro is defined.


5
#ifndef

Returns true if this macro is not defined.

6
#if

Tests if a compile time condition is true.

7
#else

The alternative for #if.

8
#elif

#else and #if in one statement.

9
#endif

Ends preprocessor conditional.

10
#error

Prints error message on stderr.

11
#pragma

Issues special commands to the compiler, using a standardized method.

Preprocessors Examples

Analyze the following examples to understand various directives.

#define MAX_ARRAY_LENGTH 20

This directive tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20.
Use #define for constants to increase readability.
#include <stdio.h>
#include "myheader.h"

These directives tell the CPP to get stdio.h from System Libraries and add the text to the
current source file. The next line tells CPP to get myheader.h from the local directory and add
the content to the current source file.

#undef FILE_SIZE
#define FILE_SIZE 42

It tells the CPP to undefine existing FILE_SIZE and define it as 42.

#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif

It tells the CPP to define MESSAGE only if MESSAGE isn't already defined.

#ifdef DEBUG
/* Your debugging statements here */
#endif

It tells the CPP to process the statements enclosed if DEBUG is defined. This is useful if you
pass the -DDEBUG flag to the gcc compiler at the time of compilation. This will define
DEBUG, so you can turn debugging on and off on the fly during compilation.

Predefined Macros

ANSI C defines a number of macros. Although each one is available for use in programming,
the predefined macros should not be directly modified.

Sr.No. Macro & Description

1
__DATE__

The current date as a character literal in "MMM DD YYYY" format.


2
__TIME__

The current time as a character literal in "HH:MM:SS" format.

3
__FILE__

This contains the current filename as a string literal.

4
__LINE__

This contains the current line number as a decimal constant.

5
__STDC__

Defined as 1 when the compiler complies with the ANSI standard.

Let's try the following example −

Live Demo

#include <stdio.h>

int main() {

printf("File :%s\n", __FILE__ );


printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );

When the above code in a file test.c is compiled and executed, it produces the following result

File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1
Preprocessor Operators

The C preprocessor offers the following operators to help create macros −

The Macro Continuation (\) Operator

A macro is normally confined to a single line. The macro continuation operator (\) is used to
continue a macro that is too long for a single line. For example −

#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
The Stringize (#) Operator

The stringize or number-sign operator ( '#' ), when used within a macro definition, converts a
macro parameter into a string constant. This operator may be used only in a macro having a
specified argument or parameter list. For example −

Live Demo

#include <stdio.h>

#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")

int main(void) {
message_for(Carole, Debra);
return 0;
}

When the above code is compiled and executed, it produces the following result −

Carole and Debra: We love you!


The Token Pasting (##) Operator

The token-pasting operator (##) within a macro definition combines two arguments. It permits
two separate tokens in the macro definition to be joined into a single token. For example −

Live Demo

#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}

When the above code is compiled and executed, it produces the following result −

token34 = 40

It happened so because this example results in the following actual output from the
preprocessor −

printf ("token34 = %d", token34);

This example shows the concatenation of token##n into token34 and here we have used
both stringize and token-pasting.

The Defined() Operator

The preprocessor defined operator is used in constant expressions to determine if an identifier


is defined using #define. If the specified identifier is defined, the value is true (non-zero). If
the symbol is not defined, the value is false (zero). The defined operator is specified as follows

Live Demo
#include <stdio.h>

#if !defined (MESSAGE)


#define MESSAGE "You wish!"
#endif

int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}

When the above code is compiled and executed, it produces the following result −

Here is the message: You wish!


Parameterized Macros

One of the powerful functions of the CPP is the ability to simulate functions using
parameterized macros. For example, we might have some code to square a number as follows

int square(int x) {
return x * x;
}

We can rewrite above the code using a macro as follows −

#define square(x) ((x) * (x))

Macros with arguments must be defined using the #define directive before they can be used.
The argument list is enclosed in parentheses and must immediately follow the macro name.
Spaces are not allowed between the macro name and open parenthesis. For example −

Live Demo

#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}

When the above code is compiled and executed, it produces the following result −

Max between 20 and 10 is 20

Conditional Compilation
In C programming language, several directives control the selective compilation of portions
of the program code. They are as follows −

 #if
 #else
 #elif
 #endif
The general form of #if is as follows −

#if constant_expression

statement sequence

#endif

#else works much like the C keyword else.

#elif means "else if" and establishes an if else-if compilation chain.

Amongst other things, #if provides an alternative method of "commenting out" code.

For example,

#if 0

printf("#d", total);
#endif

Here, the compiler will ignore printf("#d", total);

#ifdef and #ifndef

#ifdef means "if defined", and is terminated by an #endif.

#indef means "if not defined".

#undef

#undef removes a previously defined definition.

#line

#line changes the contents of __LINE__ which contains the line number of the currently
compiled code and __FILE__ which is a string that contains the name of the source file being
compiled. Both of which are predefined identifiers in the compiler.

#pragma

The #pragma directive is an implementation-defined directive which allows the various


instructions to be given to the compiler.

Example
Following is the C program to demonstrate #ifdef, #ifndef , #else and #endif −

Live Demo

# include <stdio.h>

# define a 10

void main(){

#ifdef a

printf("
Hello I am here..");

#endif
#ifndef a

printf("
Not defined ");

#else

printf("
R u There ");

#endif

Output
When the above program is executed, it produces the following result −

Hello I am here..

R u There

Parameter Passing in C
When a function gets executed in the program, the execution control is transferred from
calling-function to called function and executes function definition, and finally comes back to
the calling function. When the execution control is transferred from calling-function to called-
function it may carry one or number of data values. These data values are called as parameters.

In C, there are two types of parameters and they are as follows...

 Actual Parameters
 Formal Parameters

The actual parameters are the parameters that are speficified in calling function. The formal
parameters are the parameters that are declared at called function. When a function gets
executed, the copy of actual parameter values are copied into formal parameters.

In C Programming Language, there are two methods to pass parameters from calling function
to called function and they are as follows...
 Call by Value
 Call by Reference

In call by value parameter passing method, the copy of actual parameter values are
copied to formal parameters and these formal parameters are used in called
function. The changes made on the formal parameters does not effect the values
of actual parameters. That means, after the execution control comes back to the
calling function, the actual parameter values remains same. For example consider the
following program...

Example Program

#include<stdio.h>

#include<conio.h>

void main(){

int num1, num2 ;

void swap(int,int) ; // function declaration

clrscr() ;

num1 = 10 ;

num2 = 20 ;

printf("\nBefore swap: num1 = %d, num2 = %d", num1, num2) ;

swap(num1, num2) ; // calling function

printf("\nAfter swap: num1 = %d\nnum2 = %d", num1, num2);


getch() ;

void swap(int a, int b) // called function

int temp ;

temp = a ;

a=b;

b = temp ;

Output:

In the above example program, the variables num1 and num2 are called actual parameters and
the variables a and b are called formal parameters. The value of num1 is copied into a and the
value of num2 is copied into b. The changes made on variables a and b does not effect the
values of num1 and num2.

Call by Reference

In Call by Reference parameter passing method, the memory location address of the actual
parameters is copied to formal parameters. This address is used to access the memory locations
of the actual parameters in called function. In this method of parameter passing, the formal
parameters must be pointer variables.

That means in call by reference parameter passing method, the address of the actual parameters
is passed to the called function and is recieved by the formal parameters (pointers). Whenever
we use these formal parameters in called function, they directly access the memory locations
of actual parameters. So the changes made on the formal parameters effects the values of
actual parameters. For example consider the following program...

Example Program

#include<stdio.h>

#include<conio.h>

void main(){

int num1, num2 ;

void swap(int *,int *) ; // function declaration

clrscr() ;

num1 = 10 ;

num2 = 20 ;

printf("\nBefore swap: num1 = %d, num2 = %d", num1, num2) ;

swap(&num1, &num2) ; // calling function

printf("\nAfter swap: num1 = %d, num2 = %d", num1, num2);

getch() ;

}
void swap(int *a, int *b) // called function

int temp ;

temp = *a ;

*a = *b ;

*b = temp ;

Output:

In the above example program, the addresses of variables num1 and num2 are copied to
pointer variables a and b. The changes made on the pointer variables a and b in called function
effects the values of actual parameters num1 and num2 in calling function.

You might also like