XIIB-CS (POINTERS- NOTE)


POINTERS

Key points to remember:

Pointer:

          A kind of memory variable that holds the address of other variable of same data type.

          Reference operator (&) As soon as we declare a variable, the amount of memory needed is assigned for it at a specific location in memory (its memory address).

         This reference to a variable can be obtained by preceding the identifier of a variable with an ampersand sign (&), known as reference operator, and which can be literally translated as "address of".

For example:

ted = &andy;

Consider the following code fragment:

andy = 25;

fred = andy;

ted = &andy;

The values contained in each variable after the execution of this, are shown in the following diagram:

First, we have assigned the value 25 to andy (a variable whose address in memory we have assumed to be 1776).

 

Dereference operator (*)

           a variable which stores a reference to another variable is called a pointer.

          Pointers are said to "point to" the variable whose reference they store.Using a pointer we can directly access the value stored in the variable which it points to.

          To do this, we simply have to precede the pointer's identifier with an asterisk (*), which acts as dereference operator and that can be literally translated to "value pointed by".

          Therefore, following with the values of the previous example, if we write:

beth = *ted;

(that we could read as: "beth equal to value pointed by ted") beth would take the value 25, since ted is 1776, and the value pointed by 1776 is 25. You must clearly differentiate that the expression ted refers to the value 1776, while *ted (with an asterisk * preceding the identifier) refers to the value stored at address 1776, which in this case is 25. Notice the difference of including or not including the dereference operator (I have included an explanatory commentary of how each of

these two expressions could be read):

beth = ted; // beth equal to ted ( 1776 )

beth = *ted; // beth equal to value pointed by ted ( 25 )

 

Declaring variables of pointer types

  • Due to the ability of a pointer to directly refer to the value that it points to, it becomes necessary to specify in its declaration which data type a pointer is going to point to. It is not the same thing to point to a char as to point to an int or a float.
  • The declaration of pointers follows this format: type * name; where type is the data type of the value that the pointer is intended to point to. This type is not the type of the pointer itself! but the type of the data the pointer points to. For example:
    • int * number;
    • char * character;
    • float * greatnumber;

 

Now have a look at this code:

// my first pointer

#include <iostream>

int main ()

{

int firstvalue, secondvalue;

int * mypointer;

mypointer = &firstvalue;

*mypointer = 10;

mypointer = &secondvalue;

*mypointer = 20;

cout << "firstvalue is " << firstvalue << endl;

cout << "secondvalue is " << secondvalue << endl;

return 0;

}

firstvalue is 10

secondvalue is 20

 

// more pointers

#include <iostream>

int main ()

{

int firstvalue = 5, secondvalue = 15;

int * p1, * p2;

p1 = &firstvalue; // p1 = address of firstvalue

p2 = &secondvalue; // p2 = address of secondvalue

*p1 = 10; // value pointed by p1 = 10

*p2 = *p1; // value pointed by p2 = value pointed by

p1

p1 = p2; // p1 = p2 (value of pointer is copied)

*p1 = 20; // value pointed by p1 = 20

cout << "firstvalue is " << firstvalue << endl;

cout << "secondvalue is " << secondvalue << endl;

return 0;

}

firstvalue is 10

secondvalue is 20

Notice that there are expressions with pointers p1 and p2, both with and without dereference operator (*). The meaning of an expression using the dereference operator (*) is very different from one that does not: When this operator precedes the pointer name, the expression refers to the value being pointed, while when a pointer name appears without this operator, it refers to the value of the pointer itself (i.e. the address of what the pointer is pointing to).

 

Pointers and arrays

·         The concept of array is very much bound to the one of pointer.

·         In fact, the identifier of an array is equivalent to the address of its first element, as a pointer is equivalent to the address of the first element that it points to, so in fact they are the same concept.

For example, supposing these two declarations:

int numbers [20];

int * p;

The following assignment operation would be valid:

p = numbers;

  • After that, p and numbers would be equivalent and would have the same properties.
  • The only difference is that we could change the value of pointer p by another one, whereas numbers will always point to the first of the 20 elements of type int with which it was defined.
  • Therefore, unlike p, which is an ordinary pointer, numbers is an array, and an array can be considered a constant pointer. Therefore, the following allocation would not be valid:

numbers = p;

  • Because numbers is an array, so it operates as a constant pointer, and we cannot assign values to constants.
  • Due to the characteristics of variables, all expressions that include pointers in the following example are perfectly

valid:

// more pointers

#include <iostream>

int main ()

{

int numbers[5];

int * p;

p = numbers; *p = 10;

p++; *p = 20;

p = &numbers[2]; *p = 30;

p = numbers + 3; *p = 40;

p = numbers; *(p+4) = 50;

for (int n=0; n<5; n++)

cout << numbers[n] << ", ";

return 0;

}

10, 20, 30, 40, 50,

In arrays we used brackets ([]) to specify the index of an element of the array to which we wanted to refer. Well, these bracket sign operators [] are also a dereference operator known as offset operator. They dereference the variable they follow just as * does, but they also add the number between brackets to the address being dereferenced. For example:

a[5] = 0; // a [offset of 5] = 0

*(a+5) = 0; // pointed by (a+5) = 0

These two expressions are equivalent and valid both if a is a pointer or if a is an array.

 

Pointer initialization

  • When declaring pointers we may want to explicitly specify which variable we want them to point to:

int number;

int *tommy = &number;

The behavior of this code is equivalent to:

int number;

int *tommy;

tommy = &number;

When a pointer initialization takes place we are always assigning the reference value to where the pointer points (tommy), never the value being pointed (*tommy).

 

As in the case of arrays, the compiler allows the special case that we want to initialize the content at which the pointer points with constants at the same moment the pointer is declared:

char * terry = "hello";

In this case, memory space is reserved to contain "hello" and then a pointer to the first character of this memory block is assigned to terry. If we imagine that "hello" is stored at the memory locations that start at addresses 1702, we can represent the previous declaration as: It is important to indicate that terry contains the value 1702, and not 'h' nor hello", although 1702 indeed is the address of both of these. For example, we can access the fifth element of the array with any of these two expression:

*(terry+4)

terry[4]

Both expressions have a value of 'o' (the fifth element of the array).

 

Pointer arithmetics

  • To conduct arithmetical operations on pointers is a little different than to conduct them on regular integer data types.
  • To begin with, only addition and subtraction operations are allowed to be conducted with them, the others make no sense in the world of pointers.
  • But both addition and subtraction have a different behavior with pointers according to the size of the data type to which they point.

For example, let's assume that in a given compiler for a specific machine,

char takes 1 byte, short takes 2 bytes and long takes 4.

Suppose that we define three pointers in this compiler:

char *mychar;

short *myshort;

long *mylong;

and that we know that they point to memory locations 1000, 2000 and 3000 respectively.

So if we write:

mychar++;

myshort++;

mylong++;

mychar, as you may expect, would contain the value 1001. But not so obviously, myshort would contain the value 2002, and mylong would contain 3004, even though they have each been increased only once. The reason is that when adding one to a pointer we are making it to point to the following element of the same type with which it has been defined, and therefore the size in bytes of the type pointed is added to the pointer. This is applicable both when adding and subtracting any number to a pointer. It would happen exactly the same if we write:

mychar = mychar + 1;

myshort = myshort + 1;

mylong = mylong + 1;

Both the increase (++) and decrease (--) operators have greater operator precedence than the dereference operator (*), but both have a special behavior when used as suffix (the expression is evaluated with the value it  had before being increased). Therefore, the following expression may lead to confusion:

*p++

Because ++ has greater precedence than *, this expression is equivalent to *(p++). Therefore, what it does is to increase the value of p (so it now points to the next element), but because ++ is used as postfix the whole expression is evaluated as the value pointed by the original reference (the address the pointer pointed to before being increased).

Notice the difference with:

(*p)++

Here, the expression would have been evaluated as the value pointed by p increased by one. The value of p (the pointer itself) would not be modified (what is being modified is what it is being pointed to by this pointer).

If we write:

*p++ = *q++;

Because ++ has a higher precedence than *, both p and q are increased, but because both increase operators (++) are used as postfix and not prefix, the value assigned to *p is *q before both p and q are increased. And then both are increased. It would be roughly equivalent to:

*p = *q;

++p;

++q;

Like always, I recommend you to use parentheses () in order to avoid unexpected results and to give more legibility to the code.

 

Pointers to pointers

  • C++ allows the use of pointers that point to pointers, that these, in its turn, point to data (or even to other pointers). In order to do that, we only need to add an asterisk (*) for each level of reference in their declarations:

char a;

char * b;

char ** c;

a = 'z';

b = &a;

c = &b;

This, supposing the randomly chosen memory locations for each variable of 7230, 8092 and 10502, could be represented as: The value of each variable is written inside each cell; under the cells are their respective addresses in memory. The new thing in this example is variable c, which can be used in three different levels of indirection, each one of them would correspond to a different value:

c has type char** and a value of 8092

*c has type char* and a value of 7230

**c has type char and a value of 'z'

 

Null pointer

  • A null pointer is a regular pointer of any pointer type which has a special value that indicates that it is not pointing to any valid reference or memory address.
  • This value is the result of type-casting the integer value zero to any pointer type.

int * p;

p = 0; // p has a null pointer value

  • Do not confuse null pointers with void pointers.
  • A null pointer is a value that any pointer may take to represent that it is pointing to "nowhere", while a void pointer is a special type of pointer that can point to somewhere without a specific type. One refers to the value stored in the pointer itself and the other to the type of data it points to.

 

Dynamic Memory

  • Until now, in all our programs, we have only had as much memory available as we declared for our variables, having the size of all of them to be determined in the source code, before the execution of the program.
  • But, what if we need a variable amount of memory that can only be determined during runtime? For example, in the case that we need some user input to determine the necessary amount of memory space.
  • The answer is dynamic memory, for which C++ integrates the operators new and delete.

 

Operators new and new[]

  • In order to request dynamic memory we use the operator new.
  • new is followed by a data type specifier and -if a sequence of more than one element is required- the number of these within brackets []. It returns a pointer to the beginning of the new block of memory allocated. Its form is:

pointer = new type

pointer = new type [number_of_elements]

 

The first expression is used to allocate memory to contain one single element of type type. The second one is used to assign a block (an array) of elements of type type, where number_of_elements is an integer value representing the amount of these. For example:

int * bobby;

bobby = new int [5];

In this case, the system dynamically assigns space for five elements of type int and returns a pointer to the first element of the sequence, which is assigned to bobby. Therefore, now, bobby points to a valid block of memory with space for five elements of type int. The first element pointed by bobby can be accessed either with the expression bobby[0] or the expression *bobby. The second element can be accessed either with bobby[1] or *(bobby+1) and so on...

 

Operators delete and delete[ ]

  • Since the necessity of dynamic memory is usually limited to specific moments within a program, once it is no longer needed it should be freed so that the memory becomes available again for other requests of dynamic memory.
  • This is the purpose of the operator delete, whose format is:
    • delete pointer;
    • delete [] pointer;
  • The first expression should be used to delete memory allocated for a single element, and the second one for memory allocated for arrays of elements.

 

Practice :- Rewrite the following codes after removing errors, if any, in the following snippet. Explain each error.

void main()

{

const int i = 20;

const int * const ptr = &i;

(*ptr)++;

int j = 15;

ptr = &j;

}

 

Practice: Give the output of the following program:

void main()

{

char *p = “School”;

char c;

c = ++ *p ++;

cout<<c;

}

 

Practice:  Give the output of the following program:

void main()

{

int x [] = { 50, 40, 30, 20, 10}:

int *p, **q, *t;

p = x;

t = x + 1;

q = &t;

cout << *p << “,” << **q << “,” << *t++;

}

 

Practice: Give the output of the following program( Assume all necessary header files are included):

void main( )

{

char * x = “WorLd CuP”;

char c;

c = ++ *x ++;

cout<<c;

}

Practice: Give the output of the following program(Assume all necessary header files are included) :

void main( )

{

char *x = “WorLD CuP”;

char c;

c = ( *(x+1) ) ++ ;

cout<<c;

}

 

Practice. What will be the output of the program( Assume all necessary header files are included) :

void print (char * p )

{

p = "Comp";

cout<<"value is "<<p<<endl;

}

void main( )

{

char * x = "Class XII";

print(x);

cout<<"new value is "<<x<<endl;}

 

Practice: Give output of following code fragment:

char *msg = “a ProFile”;

for (int i = 0; i < strlen (msg); i++)

if (islower(msg[i]))

msg[i] = toupper (msg[i]);

else

if (isupper(msg[i]))

if( i % 2 != 0)

msg[i] = tolower (msg[i-1]);

else

msg[i--];

cout << msg << endl;

 

 

 

 

 

Popular posts from this blog

XII- CS-- SOLVED SQL queries (SQL PRACTICAL ASSIGNMENS)

SQL--HOME ASSIGNMENTS(XII Class)

Python-MySQL Connectivity