Page 1 of 1

Functions in C and C++, Part II: Argument Passing and Arrays Rate Topic: ***** 2 Votes

#1 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6064
  • View blog
  • Posts: 23,520
  • Joined: 23-August 08

Posted 23 December 2010 - 05:24 AM

*
POPULAR

C and C++ Pass-By-Pointer

As mentioned in Part I of the tutorial series, what is passed to a function by its
caller is passed "by value". This means that a copy of the argument is
made prior to the call to the function, and it is that copy which
is provided to the function. As a result, any actions taken with that
variable within the function only affect the copy, they do not affect the
variable in the caller. For example:
#include <stdio.h>

int squareIt(int toBeSquared);

int main(void)
{
    int valueToSquare = 5;
    int squaredValue = squareIt(valueToSquare);
    printf("%d squared is %d\n", valueToSquare, squaredValue);

    return 0;
}

int squareIt(int toBeSquared)
{
    /* Operate directly on the passed-in value
        and return it */
    toBeSquared = toBeSquared * toBeSquared;
    return toBeSquared;
}


which yields:

./sq
5 squared is 25


demonstrating that the valueToSquare variable which was passed was not
in fact modified by the function.

So, how to get around this? Well, think about it this way: if you're
going to the coffee maker to get some nice, fresh nectar of the gods to
bring back to your programming session, you need a container in which to
carry it, right? Well in C and C++ the container is that pointer you
know and love!

A pointer variable consists the address in memory of the variable to which
it's pointing. Memory is a container of sorts, isn't it? So, if we
wanted to make that squareIt function above actually operate on
the value passed-in, we can change the function prototype to return
void, as it will be returning the modified variable passed
instead, and change the argument to be a pointer to an int,
like so:

void squareIt(int *toBeSquared);


Where we've changed the prototype, we also have to change the
implementation of the function, which we do like so:

void squareIt(int *toBeSquared)
{
    /* Remember, we are passed the CONTAINER
        for the variable, so in order to actually work
        with the variable's contents, we must get the
        content from the container. This is done through
        the pointer dereference operator, * */
    *toBeSquared = (*toBeSquared) * (*toBeSquared);
}


And with the change in prototype comes the need to change how the
function is called. To provide a pointer to a memory location, you
prefix the variable with the address-of operator, & in the
function call, so our program above would now look like:

#include <stdio.h>

void squareIt(int *toBeSquared);

int main(void)
{
    int valueToSquare = 5;

    /* Because we're changing the value of the variable in the
        function, we need to print its initial value before we call 
        the function */
    printf("%d squared is ", valueToSquare);
   
    /* Pass the address of the variable to the function.
        Note we have no = in this function call; the function
        does not return anything directly to the caller, so there's
        no need; in fact, it should cause a compilation error. */
    squareIt(&valueToSquare);

    /* And print the result */
    printf("%d\n", valueToSquare);

    return 0;
}

void squareIt(int *toBeSquared)
{
    /* Operate directly on the passed-in value, which
        means there's nothing to explcitly return. */
    *toBeSquared = (*toBeSquared) * (*toBeSquared);
}


Running this program results in the identical output:

./sq2
5 squared is 25



C++ Pass-By-Reference

Now C++ -- not C -- has a nifty little feature called
pass-by-reference, which hides that icky pointer notation from you,
making your code somewhat easier to read. To use this in C++, you will
use that same & operator, only this time it's part of the
arguments in the function signature, rather than on the argument
provided by the caller.

void squareIt(int &toBeSquared);


So the equivalent program in C++ (note, the
program above is still valid C++) would be:

#include <iostream>

// Pass the variable by reference
void squareIt(int &toBeSquared);

int main()
{
    int valueToSquare = 5;
    std::cout << valueToSquare << " squared is ";
   
    // Note, we pass the variable AS IS, no & necessary!  
    squareIt(valueToSquare);

    std::cout << valueToSquare << std::endl;
}

void squareIt(int &toBeSquared)
{
    // Here we get rid of the icky pointer notation
    // Note that using *= is the same as the above
    // toBeSquared = toBeSquared * toBeSquared;
    toBeSquared *= toBeSquared;
}


Again, running this program the result is the same:

./sq3
5 squared is 25


Functions and Passing Arrays

Arrays would seem to be a special case when passing these to a
function; in fact, arrays are automatically passed by pointer. When
passed to a function, an array "decays" to a pointer. In other words, if
you have a function which is declared like this:

void printArray(int myArray[], int size)


the compiler is actually implementing it for you like this:

void printArray(int *myArray, int size)


Now, notice in both of these function prototypes I have passed a second
variable, size. This is required when passing arrays
because the array is being treated as a pointer within the
function. If we're going to operate on the array within the function, we
need to know the bounds of the array, else we're going to go off into
memory we don't own and cause the program to crash.

Also, because the array is treated as a pointer within the function,
then any changes you make to the array will be reflected in the variable
passed by the calling routine.

Here's how you would implement and call the function above:

#include <stdio.h>

void printArray(int myArray[], int size);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[5] = { 2, 4, 6, 8, 10 };

    /* Notice we do NOT use the array brackets here when passing
        the array! */
    printArray(arrayOfInts, 5);
    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        printf("Element %d: %d\n", i, myArray[i]);

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i; 
    }
}



Compiling and running this program yields:
./arrfunc 
Element 0: 2
Element 1: 4
Element 2: 6
Element 3: 8
Element 4: 10



So now we can demonstrate that you can modify the array within the
function and have it reflected in the calling function. We'll keep the
printArray function for its utility, and add a new function,
squareArray, which will square each of the elements within the
array.

#include <stdio.h>

void printArray(int myArray[], int size);
void squareArray(int myArray[], int size);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[5] = { 2, 4, 6, 8, 10 };

    /* Notice we do NOT use the array brackets here when passing                
       the array! */
    printf("Array before:\n");
    printArray(arrayOfInts, 5);

    squareArray(arrayOfInts, 5);
    printf("Array after:\n");
    printArray(arrayOfInts, 5);

    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        printf("Element %d: %d\n", i, myArray[i]);

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i;
    }
}

void squareArray(int myArray[], int size)
{
    int i;
    for (i = 0; i < size; ++i)
    {
        myArray[i] *= myArray[i];
    }
}



Compiling and running this yields:
Array before:
Element 0: 2
Element 1: 4
Element 2: 6
Element 3: 8
Element 4: 10
Array after:
Element 0: 4
Element 1: 16
Element 2: 36
Element 3: 64
Element 4: 100



Functions and Returning Arrays

But what if you don't want to modify the passed-in array, instead
generating a different array? Well, another gotcha of sorts with
functions and arrays is that there is no way to return an array -- as
such -- from a function; instead, you either pass in another array which
you populate in the function, or you return a pointer to the first
element of the array you create in the function.

In the second case, keep in mind what we said in Part I of
this tutorial: variables created within the function only exist for the
scope of the function in which they're created. Therefore, if you wish
to use the second case of returning a pointer, the pointer must point to
dynamically-allocated memory -- memory allocated through the use of
malloc or calloc in C, or new in C++ -- which
must therefore be de-allocated when you no longer need it through the
use of free in C or delete [] in C++, or a pointer to
a static array created within the function.

First we'll demonstrate passing a second array to the function, again
using our squareArray function, only modified to support
passing in the second array. Its prototype will look like this:

void squareArray(int toBeSquared[], int size, int
arraySquared[]);


We will create a second, uninitialized array in the main
function to receive the squared data:
#include <stdio.h>

void printArray(int myArray[], int size);
void squareArray(int toBeSquared[], int size, int squaredArray[]);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[5] = { 2, 4, 6, 8, 10 };

    /* An uninitialized array */
    int arrayOfSquaredInts[5];

    /* Notice we do NOT use the array brackets here when passing                
       the array! */
    printf("Initial Array before:\n");
    printArray(arrayOfInts, 5);

    /* Pass the second array to the function */
    squareArray(arrayOfInts, 5, arrayOfSquaredInts);

    printf("Initial Array after:\n");
    printArray(arrayOfInts, 5);

    printf("Squared array:\n");
    printArray(arrayOfSquaredInts, 5);

    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        printf("Element %d: %d\n", i, myArray[i]);

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i;
    }
}

void squareArray(int toBeSquared[], int size, int squaredArray[])
{
    int i;
    for (i = 0; i < size; ++i)
    {
        squaredArray[i] = toBeSquared[i] * toBeSquared[i];
    }
}



Compiling and running this yields:
Initial Array before:
Element 0: 2
Element 1: 4
Element 2: 6
Element 3: 8
Element 4: 10
Initial Array after:
Element 0: 2
Element 1: 4
Element 2: 6
Element 3: 8
Element 4: 10
Squared array:
Element 0: 4
Element 1: 16
Element 2: 36
Element 3: 64
Element 4: 100



Next, let's demonstrate returning the new array as a
dynamically-allocated pointer to the first element. We will change the
prototype to return an int * to the caller, like so:

int *squareArray(int toBeSquared[], int size);


#include <stdio.h>
#include <stdlib.h> /* Required for malloc/free */

void printArray(int myArray[], int size);
int *squareArray(int toBeSquared[], int size);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[5] = { 2, 4, 6, 8, 10 };

    /* An uninitialized pointer */
    int *arrayOfSquaredInts;

    /* Notice we do NOT use the array brackets here when passing                
       the array! */
    printf("Initial Array before:\n");
    printArray(arrayOfInts, 5);

    /* Pass the second array to the function */
    arrayOfSquaredInts = squareArray(arrayOfInts, 5);

    printf("Initial Array after:\n");
    printArray(arrayOfInts, 5);

    printf("Squared array:\n");
    printArray(arrayOfSquaredInts, 5);

    /* We used malloc to create the array, so we must
       use free to return the memory we not longer need */
    free(arrayOfSquaredInts);
    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        printf("Element %d: %d\n", i, myArray[i]);

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i;
    }
}

int *squareArray(int toBeSquared[], int size)
{
    /*                                                                          
       Dynamically allocate an array of the appropriate size.                   
       Note that the size we need MUST take into account the                    
       size of the variable to which we're pointing.                            
       sizeof(*squaredArray) gets the size of the data type to                  
       which squaredArray is pointing.                                          
    */
    int *squaredArray = malloc(size * sizeof(*squaredArray));

    int i;
    for (i = 0; i < size; ++i)
    {
        squaredArray[i] = toBeSquared[i] * toBeSquared[i];
    }

    return squaredArray;
}



If you compile and run this, you will notice the output is the same as
the above.

I'll provide the equivalent C++ solution here as well.

#include <iostream>

void printArray(int myArray[], int size);
int *squareArray(int toBeSquared[], int size);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[5] = { 2, 4, 6, 8, 10 };

    /* An uninitialized pointer */
    int *arrayOfSquaredInts;

    /* Notice we do NOT use the array brackets here when passing                
       the array! */
    std::cout << "Initial Array before:" << std::endl;
    printArray(arrayOfInts, 5);

    /* Pass the second array to the function */
    arrayOfSquaredInts = squareArray(arrayOfInts, 5);

    std::cout << "Initial Array after" << std::endl;
    printArray(arrayOfInts, 5);

    std::cout << "Squared array:" << std::endl;
    printArray(arrayOfSquaredInts, 5);

    // Again, because we allocated memory with new in the 
    // function, we must have a correponding delete to 
    // return the no longer needed memory to the system.
    // Note the use of delete[], because we allocated an array                  
    delete [] arrayOfSquaredInts;

    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        std::cout << "Element " << i << ": " << myArray[i] << std::endl;

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i;
    }
}

int *squareArray(int toBeSquared[], int size)
{
    // Dynamically allocate an array of the appropriate size.                   
    int *squaredArray = new int[size];

    int i;
    for (i = 0; i < size; ++i)
    {
        squaredArray[i] = toBeSquared[i] * toBeSquared[i];
    }

    return squaredArray;
}



Finally, there's the static variable solution. This is not
particularly helpful in most situations, as a static variable means that
the memory for the variable is created at compile time -- so we must know
the array's size when creating the variable -- and the variable exists for
the duration of the program. The need for this actually removes the need
to pass the size of the array to the function, but I will leave that in.

#include <stdio.h>

/* We need to know the size of the array throughout in order                    
   to create the static variable in the function */
#define SIZE_OF_ARRAY 5

void printArray(int myArray[], int size);
int *squareArray(int toBeSquared[], int size);

int main(void)
{
    /* Initialize the array with our numbers */
    int arrayOfInts[SIZE_OF_ARRAY] = { 2, 4, 6, 8, 10 };

    /* An uninitialized pointer */
    int *arrayOfSquaredInts;

    /* Notice we do NOT use the array brackets here when passing                
       the array! */
    printf("Initial Array before:\n");
    printArray(arrayOfInts, SIZE_OF_ARRAY);

    /* Pass the second array to the function */
    arrayOfSquaredInts = squareArray(arrayOfInts, SIZE_OF_ARRAY);

    printf("Initial Array after:\n");
    printArray(arrayOfInts, SIZE_OF_ARRAY);

    printf("Squared array:\n");
    printArray(arrayOfSquaredInts, SIZE_OF_ARRAY);

    return 0;
}

void printArray(int myArray[], int size)
{
    int i = 0;

    /* Remember, valid array indexes are from 0 to size - 1! */
    while (i < size)
    {
        printf("Element %d: %d\n", i, myArray[i]);

        /* Don't forget to increment i, or you'll have an infinite loop! */
        ++i;
    }
}

int *squareArray(int toBeSquared[], int size)
{
    /* The memory for this set aside at compile time */
    static int squaredArray[SIZE_OF_ARRAY];

    int i;
    for (i = 0; i < size; ++i)
    {
        squaredArray[i] = toBeSquared[i] * toBeSquared[i];
    }

    return squaredArray;
}



Again, the output is as shown above.

This post has been edited by JackOfAllTrades: 04 January 2012 - 08:11 AM
Reason for edit:: Thanks for jimblumberg for catching the glaring error noted below!


Is This A Good Question/Topic? 20
  • +

Replies To: Functions in C and C++, Part II: Argument Passing and Arrays

#2 darek9576  Icon User is offline

  • D.I.C Lover

Reputation: 198
  • View blog
  • Posts: 1,689
  • Joined: 13-March 10

Posted 09 March 2011 - 05:34 PM

Awesome tutorial.
Was This Post Helpful? 0
  • +
  • -

#3 beginningcguy  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 36
  • Joined: 23-February 11

Posted 17 March 2011 - 03:09 PM

pretty helpful, better than my teacher instruction for sure. Thanks.
Was This Post Helpful? 0
  • +
  • -

#4 sara-92  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 12-November 11

Posted 01 January 2012 - 12:50 PM

Thank you for this tutorial :flowers:

It is very helpful ;)

And also easy to read and understand ^^

Was This Post Helpful? 0
  • +
  • -

#5 jimblumberg  Icon User is online

  • member icon


Reputation: 4076
  • View blog
  • Posts: 12,585
  • Joined: 25-December 09

Posted 03 January 2012 - 09:37 AM

In your C++ solution for Functions and Returning arrays you forgot to return the pointer from squareArray.

int *squareArray(int toBeSquared[], int size)
{
    // Dynamically allocate an array of the appropriate size.                   
    int *squaredArray = new int[size];

    int i;
    for (i = 0; i < size; ++i)
    {
        squaredArray[i] = toBeSquared[i] * toBeSquared[i];
    }
    // Add this next line.
    return squaredArray;
}



Jim
Was This Post Helpful? 1
  • +
  • -

#6 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6064
  • View blog
  • Posts: 23,520
  • Joined: 23-August 08

Posted 04 January 2012 - 08:10 AM

Whoopsie! Thanks, I'll fix it.
Was This Post Helpful? 0
  • +
  • -

#7 axnjxn  Icon User is offline

  • D.I.C Head

Reputation: 14
  • View blog
  • Posts: 144
  • Joined: 04-February 12

Posted 04 February 2012 - 12:09 PM

Great tutorial. Did a much better job than the 16 pages dedicated to pointers by my C++ e-book! Thanks.

-axnjxn
Was This Post Helpful? 0
  • +
  • -

#8 Roonil Wazlib  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 30
  • Joined: 03-August 12

Posted 04 August 2012 - 03:54 AM

Sorry for the random post, but this was a great help.
Was This Post Helpful? 0
  • +
  • -

#9 jain87  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 17-November 12

Posted 21 November 2012 - 10:23 AM

Awesome and very very helpfull! Thanks!
Was This Post Helpful? 0
  • +
  • -

#10 Gabarel  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 35
  • Joined: 19-August 13

Posted 30 August 2013 - 06:36 AM

Very good tutorial, later I want to play around with the examples on arrays and malloc.
Just only a question, I didn't get which can be the practical advantage on passing a pointer to a value, instead of its copy, that is by value, to the function.
By definition you got, in the case of a declaration of an array of variables we are forced to draw a pointer to a function because it cannot return an array, and they are already treated from compiler as pointers since they have been decleared like array themself, and it is peaceful. Also thanks to this feature we can avoid to copy on the stack an entire array of datas which can be very very big.
But what about other cases, I mean in the case of simple variable? Does it can give more efficenty to a program as general approach?

Thank you a lot.

Gabriele.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1