0% found this document useful (0 votes)
10 views13 pages

Chapter 5 (v2) - Pointers

Chapter Five of 'Fundamentals of Programming I' discusses pointers and dynamic memory management, explaining how to use the address operator '&' to display and store variable addresses in pointer variables. It covers the definition, declaration, and operations of pointers, including pointer arithmetic and the relationship between pointers and arrays. Additionally, the chapter contrasts pointers with references, highlighting their differences and use cases in programming.

Uploaded by

susured22
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views13 pages

Chapter 5 (v2) - Pointers

Chapter Five of 'Fundamentals of Programming I' discusses pointers and dynamic memory management, explaining how to use the address operator '&' to display and store variable addresses in pointer variables. It covers the definition, declaration, and operations of pointers, including pointer arithmetic and the relationship between pointers and arrays. Additionally, the chapter contrasts pointers with references, highlighting their differences and use cases in programming.

Uploaded by

susured22
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Fundamentals of Programming I

CHAPTER FIVE
Pointers and Dynamic Memory Management
5.1 Address and Pointers
To display the address of a variable, you can use C++’s address operator, &, which means “the
address of.” When used in a non-declarative statement, the address operator placed in front of a
variable’s name refers to the address of the variable. For example, in a non-declarative statement,
&num means the address of num, &miles means the address of miles, and &foo means the
address of foo. The below program, uses the address operator to display the address of the num
variable.

#include <iostream>
using namespace std;
int main()
{
int num;
num = 22;
cout <<"The value stored in num is" <<num<<endl;
cout << "The address of num="<<&num <<endl;
return 0;
}
The output of the above program is as follows:
The value stored in num is 22
The address of num = 0012FED4

Address information changes, depending on what computer is executing the program and how
many other programs are currently loaded into memory.

Besides displaying the address of a variable, as in above program, you can store addresses in
suitably declared variables. For example, the statement

numAddr = &num;

stores the address corresponding to the variable num in the variable numAddr, as illustrated in
Figure 5.1:

Department of Software Engineering, AASTU Page 1


Fundamentals of Programming I
Similarly, the statements
d = &m;
tabPoint = &list;
chrPoint = &ch;

store addresses of the variables m, list, and ch in the variables d, tabPoint , and chrPoint , as
illustrated in Figure 5.2 below:

The variables numAddr, d, tabPoint, and chrPoint are formally called pointer variables or
pointers.

Pointers Definition and Declaration


A pointer is a variable that holds a memory address. This address is the location of another
object (typically another variable) in memory. Thus, Pointers are simply a variable used to store
the addresses of other variables and provides an indirect way of accessing data in memory. For
example, if one variable contains the address of another variable, the first variable is said to point
to the second. The following Figure 5.3 illustrates this situation:

Department of Software Engineering, AASTU Page 2


Fundamentals of Programming I
If a variable is going to hold a pointer, it must be declared as such. A pointer declaration
consists of a data type, an *, and the variable name.

The general form for declaring a pointer variable is:

Datatype *Variable_name;

where Datatype is the base type of the pointer and may be any valid data type and
Variable_name is any valid identifier.

The data type of the pointer defines what type of variables the pointer can point to. Technically,
any type of pointer can point anywhere in memory. However, all pointer arithmetic is done
relative to its data type, so it is important to declare the pointer correctly that the pointer variable
should ‘point to’ data of a specific type.

For example:
int *ptr1; // pointer to an int
double *ptr2; // pointer to a double-precision
char *ptr3; // pointer to a char
Notice that the declaration int *ptr1; specifies two things: First, the variable pointed to by
ptr1 is an integer, and second, ptr1 must be a pointer (because it’s used with the indirection
operator, *). Similarly, if the pointer ptr2 points to (contain the address of) a double-precision
number and ptr3 points to a character variable.

The value of a pointer variable is the address to which it points. For example, given the
definitions
int num;
we can write:
ptr1 = &num;

The Pointer Operators


There are two special pointer operators: * and &. The symbol & is a unary operator that the
address operator. It takes a variable as argument and returns the memory address of that
variable. (Remember, a unary operator only requires one operand).

For example,
ptr1 = &num;

The effect of the above assignment is that the address of num is assigned to ptr1. This address is
the computer's internal location of the variable. It has nothing to do with the value of num.

Department of Software Engineering, AASTU Page 3


Fundamentals of Programming I
You can think of & as returning "the address of." Therefore, the preceding assignment statement
means “ptr1 receives the address of num."

Therefore, we say that ptr1 points to num.

Figure 5.4: A simple integer pointer.

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, given that ptr1 points to num, the
expression

*ptr1
dereferences ptr1 to get to what it points to, and is therefore equivalent to num. The symbol * is
the dereference operator; it takes a pointer as argument and returns the contents of the location
to which it points.

To understand the above discussion better, assume that the variable num uses memory location
2000 to store its value. Also assume that num count has a value of 100. Then, after the preceding
assignment, ptr1 will have the value 2000. And the expression count = *ptr1; places the
value of num into count. Thus, count will have the value 100 because 100 is stored at location
2000, which is the memory address that was stored in ptr1. So you can think of * as "at
address." In this case, the preceding statement means "count receives the value at address ptr1.

In general, the type of a pointer must match the type of the data it is set to point to. A pointer of
type void*, however, will match any type. This is useful for defining pointers which may point to
data of different types, or whose type is originally unknown. A pointer may be cast (type
converted) to another type.

For example,
ptr2 = (char*)ptr1; //converts ptr1 to char pointer before assigning it to ptr2.

Regardless of its type, a pointer may be assigned the value 0 (called the null pointer). The null
pointer is used for initializing pointers, and for marking the end of pointer-based data structures
(e.g., linked lists).

Note:
Both& and * have a higher precedence than all other arithmetic operators except the unary
minus, with which they are equal.
Department of Software Engineering, AASTU Page 4
Fundamentals of Programming I

5.2 References and Pointers


At this point, you might be asking what the difference is between a pointer and a reference.
Essentially, a reference is a named constant for an address; therefore, the address named as a
reference can’t be altered/changed. Because a pointer is a variable, the address in a pointer can
be changed/altered.

For most applications, using references rather than pointers as arguments to functions is easier
and preferred. The reason is the simpler notation for locating a reference parameter, which
eliminates the address operator (&) and indirection operator (*) that are required for pointers.
Technically, references are said to be automatically dereferenced or implicitly dereferenced (the
two terms are used synonymously), and pointers must be dereferenced explicitly to locate the
value being accessed.

For example, in passing a scalar variable’s address as a function argument, references provide a
simpler notation and are usually preferred. For other situations, such as dynamically allocating
new sections of memory for additional variables as a program is running or using alternatives to
array notation pointers are required.

Reference Variables: References are used almost exclusively as formal function parameters and
return types. Nevertheless, reference variables are also available in C++. After a variable has
been declared, it can be given additional names by using a reference declaration, which has this
form:
dataType & newName = existingName;

For example, the reference declaration


double& sum = total;

equates the name sum to the name total. Both now refer to the same variable, as illustrated in
Figure 5.5 below:

Here sum is alternative name for total. After establishing another


name for a variable by using a reference declaration, the new name, referred to as an alias, can be
used in place of the original name.

Department of Software Engineering, AASTU Page 5


Fundamentals of Programming I

#include <iostream>
using namespace std;
int main()
{
double total = 20.5; // declare and initialize total
double& sum = total; // declare another name for total
cout << "sum="<<sum<<endl;
sum = 18.6; // this changes the value in total
cout << "tota l="<< total << endl;
return 0;
}

The following output is produced by Program 12.3:


sum = 20.5
total = 18.6

When constructing references, keep two points in mind. First, the reference should be of the
same data type as the variable it refers to. For example, this sequence of declarations
int num = 5;
double& numref = num; // INVALID - CAUSES A COMPILER ERROR

doesn’t equate numref to num; rather, it causes a compiler error because the two variables are
of different data types.

Second, a compiler error is produced when an attempt is made to equate a reference to a


constant. For example, the following declaration is invalid:

int& val = 5; // INVALID - CAUSES A COMPILER ERROR

After a reference name has been equated to one variable name correctly, the reference can’t be
changed to refer to another variable.

As with all declaration statements, multiple references can be declared in a single statement, as
long as each reference name is preceded by the ampersand. Therefore, the following declaration
creates two reference variables named sum and average:
double& sum = total, & average

Department of Software Engineering, AASTU Page 6


Fundamentals of Programming I

5.3 Pointer Expression


In general, expressions involving pointers conform to the same rules as other expressions. This
section examines a few special aspects of pointer expressions.

Assignment
As with any variable, you may use a pointer on the right-hand side of an assignment statement to
assign its value to another pointer. For example,

#include <stdio.h>
int main(void)
{
int x;
int *p1, *p2;
p1 = &x;
p2 = p1;
printf(" %p", p2); //print the address of x, not x's value!
return 0;
}

Bothp1and p2now point to x. The address of x is displayed by using the %p printf( ) format
specifier, which causes printf( ) to display an address in the format used by the host computer

Pointer Arithmetic and Array


There are only two arithmetic operations that you may use on pointers: addition and subtraction.
In C++ one can add an integer quantity to or subtract an integer quantity from a pointer. This is
frequently used by programmers and is called pointer arithmetic. Pointer arithmetic is not the
same as integer arithmetic, because the outcome depends on the size of the object pointed to.

For example, suppose that an int is represented by 4 bytes. Now, given


char *str = "HELLO";
int nums[] = {10, 20, 30, 40};
int *ptr = &nums[0]; // pointer to first element

str++ advances str by one char (i.e., one byte) so that it points to the second character of
"HELLO", whereas ptr++ advances ptr by one int (i.e., four bytes) so that it points to the second
element of nums. Figure 5.Error! Bookmark not defined. illustrates this diagrammatically.

Department of Software Engineering, AASTU Page 7


Fundamentals of Programming I

Figure 5.6 Pointer arithmetic.

H E L L O \0 10 20 30 40

str ptr

str++ ptr++

It follows, therefore, that the elements of "HELLO" can be referred to as *str, *(str + 1),
*(str + 2), etc. Similarly, the elements of nums can be referred to as *ptr, *(ptr + 1),
*(ptr + 2), and *(ptr + 3).

Another form of pointer arithmetic allowed in C++ involves subtracting two pointers of the same
type. For example:
int *ptr1 = &nums[1];
int *ptr2 = &nums[3];
int n = ptr2 - ptr1; // n becomes 2

Although pointers are simply, by definition, variables used to store addresses, there’s also a
direct and intimate relationship between array names and pointers. The Figure 5.7 illustrates the
storage of a one-dimensional array named grade, which contains five integers. Each integer
requires 4 bytes of storage.

Figure 5.7: The grade array in storage

Using subscripts, the fourth element in the grade array is referred to as grade[3] . The use of a
subscript, however, conceals the computer’s extensive use of addresses. Internally, the computer
immediately uses the subscript to calculate the array element’s address based on both the array’s
starting address and the amount of storage each element uses. Calling the fourth element
grade[3] forces the compiler to make this address computation:
&grade[3] = &grade[0] + (3 * sizeof(int))

Remembering that the address operator, &, means “the address of,” this statement is read “the
address of grade[3] equals the address of grade[0] plus 12.”

Department of Software Engineering, AASTU Page 8


Fundamentals of Programming I
The Figure 5.8 below illustrates the address computation used to locate grade[3] .

Figure 5.8: Using a subscript to obtain an address

Recall that a pointer is a variable used to store an address. If you create a pointer to store the
address of the first element in the grade array, you can mimic the computer’s operation to access
the array elements. Then, using the * operator, you can use the address in the pointer to access
each array element. For example, if you store the address of grade[0] in a pointer named gPtr by
using the assignment statement
gPtr = &grade[0];

the expression *gPtr (which means “the variable pointed to by gPtr”) references grade[0] , as
shown in Figure 5.9 below:

Figure 5.9: An offset of 3 from the address in gPtr

Figure 5.10: The relationship between array elements and pointers

Department of Software Engineering, AASTU Page 9


Fundamentals of Programming I
Pointer arithmetic is very handy when processing the elements of an array. Listing 7.1 shows as
an example a string copying function similar to strcpy.
Listing 5.1
1 void CopyString (char *dest, char *src)
2 {
3 while (*dest++ = *src++)
4 ;
5 }

Annotation

3: The condition of this loop assigns the contents of src to the contents of dest and then
increments both pointers. This condition becomes 0 when the final null character of src
is copied to dest.

In turns out that an array variable (such as nums) is itself the address of the first element of the
array it represents. Hence the elements of nums can also be referred to using pointer arithmetic on
nums, that is, nums[i] is equivalent to *(nums + i). The difference between nums and ptr is that nums
is a constant, so it cannot be made to point to anything else, whereas ptr is a variable and can be
made to point to any other integer.

Listing 5.2 shows how the HighestTemp function can be improved using pointer arithmetic.

Listing 5.2
1 int HighestTemp (const int *temp, const int rows, const int cols)
2 {
3
int highest = 0;
4
5 for (register i = 0; i < rows; ++i)
6 for (register j = 0; j < cols; ++j)
7
8
if (*(temp + i * cols + j) > highest)
9 highest = *(temp + i * cols + j);
return highest;
}

Annotation

1 Instead of passing an array to the function, we pass an int pointer and two additional
parameters which specify the dimensions of the array. In this way, the function is not
restricted to a specific array size.

6 The expression *(temp + i * columns + j) is equivalent to temp[i][j] in the


previous version of this function.

Department of Software Engineering, AASTU Page 10


Fundamentals of Programming I
HighestTemp can be simplified even further by treating temp as a one-dimensional
array of row * column integers. This is shown in Listing 5.3 below.

Listing 5.3
1 int HighestTemp (const int *temp, const int rows, const int cols)
2 {
3 int highest = 0;
4 for (register i = 0; i < rows * cols; ++i)
5 if (*(temp + i) > highest)
6 highest = *(temp + i);
7 return highest;
8 }

Pointer Comparison
//A program to demonstrate pointers concepts
#include<iostream.h>
void main()
{
int i=10;
int *x=&i; // x stores the address of variable i. Pointer assignment could also be done as:

cout<<"The pointer x is stored at the memory address: "<<&x;


cout<<"\nThe pointer x stores the memory address of i: "<<x;

//Contrast difference between memory address of pointer and memory address it stores.
cout<<"\nThe value of i accessed through pointer x is: "<<*x;

// Now manipulate the value of i using pointer x.


*x = *x +1; //increament i by one
cout<<"\ni (through pointer x) = "<<*x<<" which equals i (direct access) "<<i;

//pointer arithmetic
++x;
cout<<"\nThe value of x after increament is: "<<x;
}

Department of Software Engineering, AASTU Page 11


Fundamentals of Programming I

5.4 Dynamic Memory


In addition to the program stack (which is used for storing global variables and stack frames for
function calls), another memory area, called the heap, is provided. The heap is used for
dynamically allocating memory blocks during program execution. As a result, it is also called
dynamic memory. Similarly, the program stack is also called static memory.

Two operators are used for allocating and deallocating memory blocks on the heap. The new
operator takes a type as argument and allocated a memory block for an object of that type. It
returns a pointer to the allocated block. For example,

int *ptr = new int;


char *str = new char[10];
allocate, respectively, a block for storing a single integer and a block large enough for storing an
array of 10 characters.

Memory allocated from the heap does not obey the same scope rules as normal variables. For
example, in

void Foo (void)


{
char *str = new char[10];
//...
}

when Foo returns, the local variable str is destroyed, but the memory block pointed to by str is
not. The latter remains allocated until explicitly released by the programmer.
The delete operator is used for releasing memory blocks allocated by new. It takes a pointer as
argument and releases the memory block to which it points. For example:

delete ptr; // delete an object


delete [] str; // delete an array of objects

Note that when the block to be deleted is an array, an additional [] should be included to indicate
this.

Should delete be applied to a pointer which points to anything but a dynamically-allocated


object (e.g., a variable on the stack), a serious runtime error may occur. It is harmless to apply
delete to the 0 pointer.

Dynamic objects are useful for creating data which last beyond the function call which creates
them. Listing 5.4 illustrates this using a function which takes a string parameter and returns a
copy of the string.

Department of Software Engineering, AASTU Page 12


Fundamentals of Programming I
Listing 5.4
1 #include <string.h>

2 char* CopyOf (const char *str)


3 {
4 char *copy = new char[strlen(str) + 1];

5 strcpy(copy, str);
6 return copy;
7 }

Annotation (analysis)
1 This is the standard string header file which declares a variety of functions for
manipulating strings.
4 The strlen function (declared in string.h) counts the characters in its string
argument up to (but excluding) the final null character. Because the null
character is not included in the count, we add 1 to the total and allocate an array
of characters of that size.
5 The strcpy function (declared in string.h) copies its second argument to its
first, character by character, including the final null character.

Because of the limited memory resources, there is always the possibility that dynamic memory
may be exhausted during program execution, especially when many large blocks are allocated
and none released. Should new be unable to allocate a block of the requested size, it will return 0
instead. It is the responsibility of the programmer to deal with such possibilities.

Practical Exercises (Take look at your text books):

************* End *************

Department of Software Engineering, AASTU Page 13

You might also like