C++ Ripped Apart :: Section 5
Author: Mike Ware
Website: [warebiz] :: "The Programmer's Domain" - http://warebiz.tripod.com
Email: warebiz@yahoo.com
Copyright © 2003 Michael Shawn Ware, All Rights Reserved.


You are here --> [Section 5 :: In-Depth Look at Functions / Pointers]

"Jump To Articles"
    --> User-Defined Functions
    --> "Built In" Functions
    --> Using Pointers
    --> Dynamically Allocating / Releasing Memory


User-Defined Functions

I mentioned the idea behind functions in an article at the beginning of this tutorial, but maybe it is a good idea to brush up on the basics before I discuss some of the more tricky aspects. Earlier, I defined a function as a subprogram that performs some specific task and perhaps returns a value to the calling function. If you wish to see the syntax of a function, [see C++ Syntax (section 1)] before continuing. Using functions in a program allows programmers to break down the solution to a program into logical steps while also writing code with fewer dependencies. A function that is defined for use in one program may be of value in another program that needs to perform the same task. If you develop practical functions that may be of value in other programs, you can place it in a user-defined header file and "include" it in other programs. This process is termed tool building and is a very important aspect of programming. Let me begin by showing an example of a function used in a program that simply determines the average of a set of scores, given that the total of all the scores and the number of scores are passed to the function as parameters.


    float calcAverage ( int totalScores, int number )
    {
            float average;
            average = float ( totalScores) / number;
            return average;
    }


This function can also be written as:


    float getAverage ( int totalScores, int number )
    {
            return ( float( totalScores) / number );
    }


In the above function, the two arguments (called function parameters) are passed to the function by value. Passing by value means that the function receives copies of the arguments and any changes to the values of the variables will not be returned to the calling function. In a case when you need to return multiple values to the calling function, you will have to pass your arguments by reference. Passing by reference means that instead of getting a copy of the argument, the function parameter will share the same memory address as the argument. In other words, when passing by reference, any changes that are made to the values of the variables will be sent back to the calling function. This is why passing by reference is useful for returning multiple values during a function call. All reference arguments are preceded by the ambersand symbol (&). An example of a function prototype that passes arguments by reference is:


	void myFunction( int& a, int& b );

Notice that type void is used to declare a function that will be returning multiple values to the calling function. We use void because the function will not be returning one sole value of a specific type, and it could be returning values that are of different types. void lets us return as many values to the calling function as we want as long as they are passed by reference. The following is a complete program that uses a function that passes values by reference.


//////////////////////////////////// USING DEV C/C++ V.4 COMPILER /////////////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

void myFunction ( int a, int& b );

void main( )
{
      int x = 5, y = 10;

      cout << "PRIOR TO FUNCTION CALL-->" << endl;
      cout << "x = " << x << endl;
      cout << "y = " << y << endl << endl << endl;

      myFunction ( x, y );

      cout << "FOLLOWING FUNCTION CALL-->" << endl;
      cout << "x = " << x << endl;
      cout << "y = " << y << endl << endl << endl;

      system("PAUSE");
      return EXIT_SUCCESS;
}// end main ( )

void myFunction ( int a, int& b)
{
      a = b * 2 + a * 2;
      b = 10 * a;
}// end myFunction ( )

//////////////////////////////////// USING DEV C/C++ V.4 COMPILER /////////////////////////////////////////////////

Here is another example: Write a function that will perform the integer division of m by n and will return the quotient and the remainder of the division.


    void divide( int m, int n, int& quotient, int& remainder )
    {
            quotient = m / n;
            remainder = m % n;

    }// end Divide( )

Something that may come in handy one day when you are faced with a problem is a function that can swap the values of two variables, whether they are of type int, float, double, char, etc. This can be accomplished by passing variables by reference.

NOTE: You can use the same name for multiple functions in a program as long as those functions have different signatures. For more info, [see Function Overloading (Section 9)].


    void swap ( int& x, int& y )
    {
            int temp;
            temp = x;
            x = y;
            y = temp;

    }// end swap ( ) for integers


    void swap ( double& x, double& y )
    {
            double temp;
            temp = x;
            x = y;
            y = temp;

    }// end swap ( ) for double


    void swap ( char& x[], char& y[], int strLength )
    {
            char temp[strLength];
            temp = x;
            x = y;
            y = temp;

    }// end swap ( )

Becoming familiar with functions will take some time, and you will only become familiar with them by using them in your programs. Be able to distinguish between an argument passed by value and an argument passed by reference. In the next section, I dig into various C++ "built in" functions that are used for specific purposes. Read on for more about "built in" functions...

"Built In" Functions

All of the following "built in" functions are defined in the header file #include <string.h> (using Dev C/C++ V.4 compiler) so be sure to include it when using these functions. These are standard functions that are built into the C++ language in order to perform some operation that would be much harder to do if a programmer had to write a user-defined function to achieve its purpose. There are many built in functions, and I will only cover 11 in this article. NOTE: the last example in this article uses pointers, which are covered in more detail further below [see, using Pointers].

strcpy ( destination, source );
Performs a char-by-char copy of source into destination; primarily used for string assignment.

strncpy ( destination, source, N );
Copies at most N characters from source into destination; the additional N argument must be present.

strcat ( string1, string2 );
Concatenates string2 onto the end of string1; sort of like saying string1 += string2 (remember that string1 must be large enough to hold string1 + string2 chars).

strncat ( string1, string2, N );
Concatenates at most N characters from string2 onto the end of string1; the additional N argument must be present.

strcmp ( string1, string2 );
Compares string1 and string2 and returns one of the following (useful for alphabetical order):
     -1 if string1 < string2
     0 if string1 = string2
     1 if string1 > string2

strncmp ( string1, string2, N );
Compares at most N characters of string1 and string2 and returns a result similar to strcmp ( ).

strcmpi ( string1, string2 );
Performs exact operation of strcmp ( ) except it is not case sensitive.

strncmpi ( string1, string2, N );
Performs exact operation of strncmp ( ) except it is not case sensitive.

strlen ( string );
Returns length of a string not including the null terminator.

strchr ( string, ch );
Returns a pointer to the first occurrence of a specified character (ch) in string; NULL is returned if ch is not found in the string.

strrchr ( string, ch );
Similar to strchr ( ) except it returns a pointer to the last (right most) occurrence of a specified character in string.


Suppose we have the following code segment in a program:


char string1[20] = "Programming";
char string2[20] = "Computer";
char string3[20] = "Programs";
char string4[20] = "proGRAMMING";

cout << strlen ( string1 );
cout << strcmp ( string1, string3 );
cout << strcmp ( string3, string2 );
cout << strncmp ( string1, string3, 6 );
cout << strcmpi ( string1, string4 );

OUTPUT produced from the above code segment: [using Dev's C/C++ V.4 compiler]

11
-1
1
0
0


Example: Suppose name contains a person's name in the following format:

First I. Last

Write a code segment that will find and print the person's middle initial.


    char midInitial;
    char* temp;

    temp = strchr( name, '.' );
    if ( temp = NULL )
            cout << "There is no middle initial found in: " << name << endl;
    else
    {
            temp--;
            midInitial = temp[0];
            cout << "Middle initial is: " << midInitial << endl << endl;
    }

A pointer is used in the above example. Pointers allow direct access to the address location of a variable in the memory of a computer. Read on for more about pointers...

Using Pointers

Pointers provide direct access to the memory addresses deep within the CPU (central processing unit) of a computer. They provide efficient methods and techniques for manipulating data in arrays, they are used in functions as reference parameters, and they are the basis for dynamic allocations of memory.

A pointer is a derived data type. Its value is any of the memory addresses available in a computer for storing and accessing data. A pointer is built on the basic concept of a pointer constant, which is drawn from the set of addresses for a computer and exists by itself. We as programmers cannot change them; we can only use them. Every variable that is declared in C++ has an associated memory location with it; the location value can be thought of as a pointer constant.

Even though addresses are constant, there is no way to know or predict what they will be during each new compile and run of a program. We must still refer to these addresses symbolically to be able to work with them. The address operator ( & ) makes it possible to associate a pointer constant with any named location in memory. For example,

    &name

points directly to the memory location designated for name.

NOTE: When the ampersand ( & ) symbol is used as a prefix to a variable name, it implies "address of" variable. When it is used as a suffix to a type, it implies that it is a reference parameter.

For int, float, double, and other types, ( two or four byte ), the first byte in memory location for the specified variable is used as the pointer. This byte is also referred to as the least significant byte ( LSB ). For char types, there is only one byte in memory so therefore that byte serves as the location in memory for a pointer.

When you store the address of a variable into another variable, the variable that holds the address is called a pointer variable. Accessing a variable through a pointer requires use of the indirection operator ( * ) before the pointer variable name. You also use the indirection operator ( * ) to define and declare pointer variables. For example,

    char *p;
    int *a;
    float *s;

One of the most common errors by novices and professionals alike is uninitialized pointers. As with variables, it is possible to initialize pointers when they are declared and defined. For example,

    int x;
    int *p = &x;

However, it is not always recommended to initialize pointers. You could also initialize a pointer to nothing by issuing:

    int *p = NULL;

Pointers are useful with functions. When dealing with passing variables to functions, you can pass by value, reference, or by a pointer which is an alternative for passing by reference. Passing by reference and by pointer will accomplish the same task: changing the values in the calling function's scope, but passing by reference is highly recommended over passing by pointer.

Example 1: Using pointers as parameters in a "swap" function.


    int a = 5;
    int b = 10;

    swap ( &a, &b );
    .
    .
    .
    void swap ( *px, *py )
    {
            int temp;
            temp = *px;
            *px = *py;
            *py = temp;
    }


Example 2: Suppose name contains a person's name in the following format:

First I. Last

Write a code segment that will find and print the person's middle initial.


    char midInitial;
    char* temp;

    temp = strchr( name, '.' );
    if ( temp = NULL )
            cout << "There is no middle initial found in: " << name << endl;
    else
    {
            temp--;
            midInitial = temp[0];
            cout << "Middle initial is: " << midInitial << endl << endl;
    }

Example 3: Demonstrates the use of various pointers.


//////////////////////////////////////  USING DEV C/C++ V.4 COMPILER //////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

const int NAMELEN = 31;
const int FLUSH = 80;

int main()
{
      char tempName[NAMELEN];
      int tempId;
      double tempSalary;

      char* empName = tempName;
      int* idNumber = &tempId;
      double* salary = &tempSalary;

      cout << "Enter employee's name: ";
      cin.get(tempName, NAMELEN);
      cin.ignore(FLUSH, '\n');
      cout << "Enter employee's identification number: ";
      cin >> tempId;
      cout << "Enter employee's yearly salary: ";
      cin >> tempSalary;

      cout << endl << endl << "RESULTS ------>" << endl;
      cout << "  " << *idNumber << "  " << tempName << endl;
      cout << "  $" << *salary << endl << endl;

      system("PAUSE");
      return EXIT_SUCCESS;
}

//////////////////////////////////////  USING DEV C/C++ V.4 COMPILER //////////////////////////////////////

Dynamically Allocating / Releasing Memory

Pointers "point" directly to internal memory locations in a computer's CPU (central processing unit). A problem can arise when you need to assign a value to a pointer that is larger than the pointer can handle. For example, consider the situation where you have a pointer named quote that is declared as follows:

    char* quote;

Now, for instance, say that quote will be given a value somewhere in the program as follows:

    quote = "C++ Rocks";

The program assigned C++ Rocks to quote during execution. Now, consider the situation which arises when we try to assign C++ Ripped Apart Rocks to quote, which currently contains C++ Rocks. With the current value of "C++ Rocks", quote has a size of 13 characters and is generally occupying 13 blocks of internal memory. The value we wish to give it, C++ Ripped Apart Rocks, has a size of 22 characters and would generally need to occupy 22 blocks of memory. Thus, if we tried to assign quote the value of C++ Ripped Apart Rocks an error would occur because quote currently does not have enough memory capacity to handle it. Therefore, there has to be some way we can handle this type of situation. The solution ultimately lies in how we can allocate new memory for quote.

We can dynamically allocate and release memory for structured data and non-structured data. Structured data is complex data which may occupy many blocks of memory such as arrays, objects, strings, and records. Non-structured data is simple data which generally occupy 1 block of memory such as integers, floating point values, double values, and character values. In our example, we are using structured data since quote is a pointer initially containing C++ Rocks, which is a series of character values or a string.

The general form for dynamically allocating or releasing memory for structured data is as follows:



type* ptrName; // --> declaration of pointer
.
.
// ptrName gets a value
.
.
delete [ ] ptrName; // --> release or "free" previously allocated memory for ptrName
.
.
ptrName = new type[size]; // --> allocate memory for "new" typed pointer; size is the size needed to handle the pointer data ptrName

The general form for dynamically allocating or releasing memory for non-structured data is as follows:



type* ptrName; // --> declaration of pointer
.
.
// ptrName gets a value
.
.
delete ptrName; // --> release or "free" previously allocated memory for ptrName
.
.
ptrName = new type; --> allocate memory for "new" typed pointer

NOTE: When allocating memory, new is an operator that accomplishes two things: it allocates the needed memory for the pointer, and it returns or results the beginning address of the allocated memory.

Our previous quote example could be handled as illustrated in the following complete program, which was written using Dev C/C++ V.4 compiler.


#include <stdlib.h>
#include <iostream.h>
#include <string.h>

int main()
{
	char* quote = "C++ Rocks";
	char* secondQuote = "C++ Ripped Apart Rocks";

	cout << "Initially, your quote was " << '"' << quote << '"';
	cout << endl << endl;

	delete [] quote;
	quote = new char[strlen(secondQuote)+1];
	strcpy(quote, secondQuote);

	cout << "Now, you prefer to say " << '"' << quote << '"';
	cout << endl << endl;

	system("PAUSE");
	return EXIT_SUCCESS;
}


Arrays are introduced in the next section. An array is necessary for programs that need to store many related values. Read on for more about using arrays in your programs...

Move on to next set of topics: [Section 6] - Arrays and Array Manipulation

Back to Top