Functions - Part 1: Basics

Introduction

Defining a Function

Function Prototyping and Function Calls

Function Arguments

Functions - Part 2: Functions and Arrays

Using Arrays as Arguments to Functions

Supposing we had a class of students, and all of them took a test. Now you want to use an array (an int array ) to keep track of the score each student achieved. ( Each array index corresponds to a student, and the value of the element corresponds to the score each student achieved. ) Now supposing we wanted to find out the total class score, we can use a loop to add all the array elements. But adding array elements is such a common task that it makes sense to design a function, with the array of student scores passed into it, to do just that. There are two ways in which we can write this function.

The First Way to Pass an Array into a Function

The first way is to write the argument array as int arr[]. Its shown in red below.

   int sumOfStudentScores( int arr[], int n )	// where arr = array name, n = size
   {
	int total = 0;
	for ( int i=0; i<n; i++ )
	    total += arr[i];
	return total;
   }

And the complete program looks like this:

   // SumArrayOne.cpp -- Showing one way of passing an array into a function,
   // to calculate the sum of the scores of a class of students

   #include <iostream>
   using namespace std;
   const int ArraySize = 8;			// number of students
   int sumOfStudentScores( int arr[], int n ); 	// function prototype

   int main()
   {
        int students[ArraySize] = { 55, 60, 60, 20, 30, 99, 100, 10 };
        int sum = sumOfStudentScores( students, ArraySize );	// calculate total score
        cout << "Total score of students: " << sum << "\n";
	return 0;
   }

   // function that returns the sum of an integer array
   int sumOfStudentScores( int arr[], int n )	// where arr = array name, n = size
   {
	int total = 0;
	for ( int i=0; i<n; i++ )
	    total += arr[i];
	return total;
   }

And the output will look like this:
Total score of students: 434

The Second Way to Pass an Array into a Functon

There is a second way to pass an array into a function. It is written like this:

   int sumOfStudentScores( int * arr, int n )	// where arr = array name, n = size
   {
	int total = 0;
	for ( int i=0; i<n; i++ )
	    total += arr[i];
	return total;
   }
Notice that int * arr has replaced int array[] in the previous function heading in the first method. If you replace the SumArrayOne program with this version, it'll still work. The next part explains why using either int * arr or int array[] in a function heading works.

Why Both Methods Work

The truth is both headings are being passed a pointer. The key is that C++ ( and C ) in most contexts treats the name of an array as if it were a pointer. C++ interprets an array name as the address of its first element. For example, if we had an array of robots...

   robots == &robots[0]	 //array name is address of first element
Notice that the SumArrayOne program above makes this function call:
int sum = sumOfStudentScores( students, ArraySize );

Since students is being passed into the function, and students is actually the address of the first element, a pointer is actually being passed into the function.

The truth is, to pass an array into a function, the address of its first element( its name, in other words) is used. Hence, using int * arr is correct.

Then why does using int arr[] work too?
This is because in C++ the notations int * arr and int arr[] have the same meaning when ( and only when ) used in a function heading or function prototype.

In fact, you could use a function prototype with int * arr and a function definition with int arr[] in the above program, and it'll still work! The same thing goes if you use a function prototype with int arr[] and a function definition with int * arr.

A summary:

Pointers and const

  1. Using a Pointer to point to a Constant Object

  2. Assigning the Address of a const variable to a pointer-to-const

  3. Not allowed to assign the address of a const variable to a regular pointer

  4. Use const When You Can

  5. Make it impossible to change the value of the pointer itself

  6. A const pointer to a const object

Functions & C-Style Strings

Pass a String as an Argument to a Function

If you want to pass a string as an argument to a function, you'll have three choices for representing the string:

  1. An array of char.
    char horses[20] = "galloping";
  2. A quoted string constant, also called a string literal.
    function("neighing")
  3. A pointer-to-char set to the address of a string.
    char * str = "trotting"

All three choices are actually type pointer-to-char ( char * ). You can use all three as arguments to string-processing functions:

int n1 = strlen(horses);	// horses is &horses[0]
int n2 = strlen(str);		// pointer to char
int n3 = strlen("neighing");	// address of first character string
You're really passing the address of the first character in the string. This implies that a string function prototype should use type char * as the type for the formal parameter representing a string.
// stringfun.cpp   --   functions with a string argument
#include 
using namespace std;
int charactersInString( const char * str, char ch );

int main()
{
	char mmm[15] = "minimum";		// string in an array

	char* wail = "ululate";			// wail points to string

	int ms = charactersInString( mmm,  'm' );
	int us = charactersInString( wail, 'u');
	cout << ms << " m characters in " << mmm << "\n";
	cout << us << " u characters in " << wail << "\n";
	return 0;
}

// this function counts the number of ch characters
// in the string str
int charactersInString( const char* str, char ch )
{
	int count = 0;
	while( *str )				// quit when *str is '\0'
	{					// This while loop actually demonstrates
		if ( *str == ch )		// a standard way to process the characters
		   count++;			// in a string
		str++;				// move pointer to next character
	}
	return count;
}
OUTPUT:
3 m characters in minimum
2 u characters in ululate

Functions that Return Strings

A function can't return a string. However it can return the address of a string.

// strgback.cpp -- a function returning a pointer to char
#include 
using namespace std;
char * buildStr( char c, int n);		   		// prototype
int main()
{
	int times;
	char ch;

	cout << "Enter a character: ";
	cin >> ch;
	cout << "Enter an integer: ";
	cin >> times;
	char* ps = buildStr( ch, times );
	cout << ps << "\n";
	delete [] ps;						// free memory
	ps = buildStr( '+', 20);				// reuse pointer
	cout << ps << "-Done-" << ps << "\n";
	delete[] ps;						// free memory
	return 0;
}

char * buildStr(char c, int n)
{
	char * pstr = new char[ n + 1 ];
	pstr[n] = '\0';						// terminate string
	while ( n-- > 0)
		pstr[n] = c;					// fill rest of string
	return pstr;
}
Output:
Enter a character: 7
Enter an integer: 46
7777777777777777777777777777777777777777777777
++++++++++++++++++++-Done-++++++++++++++++++++
Note that the variable pstr is local to the buildStr function, so when that function terminates, the memory used for pstr (but not for the string) is freed. But because the function returns the value of pstr, the program is able to access the new string through the ps pointer in main().

Functions and Structures

Although structure variables resemble arrays in that both can hold several data items, structure variables behave like basic, single valued variables when it comes to functions. You can pass structures by value, and a function can return a structure.

There are three ways you can use structures with functions:

  1. Directly pass them as Arguments

    The first and most direct way is to treat them as you would treat the basic types. Just pass them as arguments and use them, if necessary, to return values. However, the disadvantage is that if the structure is large, the space and effort involved in making a copy of a structure can increase memory requirements and slow the system down.

  2. Pass the address of a structure

    Initially, C didn't allow the passing of structure by value, so many C programmers passed the address of a structure and then using a pointer to access the structure contents. This also avoids the disadvantage of copying a large structure.

  3. Passing by reference

    C++ provides a third alternative, called passing by reference, that is not discussed here. Instead we'll see how to use the first and second ways.

Passing and Returning Structures Directly

The following example shows how to pass and return structures directly in a function. The structure travelTime represents time travelled during a journey. The program has two functions. Function sumOfTimes adds two travelTime structures to find the total time travelled between two journeys. Function showTime prints out the total time travelled.

// travel.cpp -- using structures with functions
#include 
using namespace std;
struct travelTime
{
	int hours;
	int mins;
} day1;

const int minPerHour = 60;

travelTime sumOfTimes( travelTime t1, travelTime t2 );
void showTime( travelTime t );

int main()
{
	day1.hours = 5;				// 5 hrs, 45 min
	day1.mins  = 45;

	travelTime day2 = { 4, 55 };		// 4 hrs, 55 min

	travelTime trip = sumOfTimes( day1, day2 );
	cout << "Two-day total: ";
	showTime( trip );

	travelTime day3 = { 4, 32 };		// 4 hrs, 32 min
	cout << "Three-day total: ";
	showTime( sumOfTimes( trip, day3) );

	return 0;
}

travelTime sumOfTimes( travelTime t1, travelTime t2 )
{
	travelTime answer;
	answer.hours = (t1.hours + t2.hours) + (t1.mins + t2.mins)/minPerHour;
	answer.mins  = (t1.mins + t2.mins)%minPerHour;
	return answer;
}

void showTime( travelTime t )
{
	cout << t.hours << " hours, ";
	cout << t.mins  << " minutes\n";
}

Output:

   Two-day total: 10 hours 40 minutes
   Three-day total: 15 hours 12 minutes

Passing Structure Addresses

We modify the above program to use structure addresses:

#include 
using namespace std;
struct travelTime
{
	int hours;
	int mins;
};

const int minPerHour = 60;

travelTime* sumOfTimes( travelTime* t1, travelTime* t2 );
void showTime( travelTime* t );

int main()
{
	travelTime day1 = { 5, 45 };
	travelTime day2 = { 4, 55 };

	travelTime* pday1 = &day1;

	travelTime* trip = sumOfTimes( pday1, &day2 );
	cout << "Two-day total: ";
	showTime( trip );
		
	travelTime day3 = { 4, 32 };		// 4 hrs, 32 min
	cout << "Three-day total: ";
	showTime( sumOfTimes( trip, &day3) ); 

	delete trip;

	return 0;
}

void showTime( travelTime* t )
{
	cout << t->hours << " hours "; 
	cout << t->mins  << " minutes\n";
}

travelTime* sumOfTimes( travelTime* t1, travelTime* t2 )
{
	travelTime* answer = new travelTime;
	answer->hours = t1->hours + t2->hours + ( t1->mins + t2->mins )/minPerHour;
	answer->mins  = (t1->mins  + t2->mins)%minPerHour;
	return answer;
}

The output is the same:
Output:

   Two-day total: 10 hours 40 minutes
   Three-day total: 15 hours 12 minutes