Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 14.  Arrays and Pointers


14.4. Cannot Pass Arrays to Functions

What would you expect the code in Listing 14.2 to print?

Listing 14.2.


void process_array(int ar[10])


{


  printf("[");


  for(size_t i = 0; i < dimensionof(ar); ++i)


  {


    printf("%d ", ar[i]);


  }


  printf("]\n");


}





int main()


{


  int ar1[10] =


  {


    0, 1, 2, 3, 4, 5, 6, 7, 8, 9


  };


  process_array(ar1);


  return 0;


}



If you said "[0 1 2 3 4 5 6 7 8 9]", you'll probably be very surprised to learn the actual result. In fact, this program prints "[0]". This behavior, of both C and C++, can be very surprising when you first learn about it. We have declared an array of 10 integers, given them a sequence of values, and passed them to a function declared to take an array of 10 integers. So what's the problem?

Well, C and C++ cannot pass arrays to functions! But you've been passing arrays to functions for as long as you can remember, haven't you? Alas, it's a big fib. Arrays are always converted to pointers when passed to functions in C, blithely ignoring any attempts at dimensioning on your part, and C++ has (almost completely) followed along for compatibility. In the example above, we could have declared process_array as any of the following, and the behavior would be the same:



void process_array(int ar[20]);


void process_array(int ar[]);


void process_array(int *ar);



Now I've brought it up you'll probably recall seeing main() declared with both char **argv and char *argv[]. I'm not going to delve into the history of this situation, as it's dealt with in great depth in Chapter 9 of the excellent Deep C Secrets by Peter van der Linden [Lind1994], but I am going to suggest that:

Imperfection: C and C++ arrays decay into pointers when passed to functions.[7]


[7] Actually, there are some cases where this is not true, as we see in the last section of this chapter (section 14.7).

This flexibility is very useful in many circumstances, but it can be a real source of problems. C++ (and C, in this regard) provide a tight type system, so we cannot pass a pointer to (or array of) double to something expecting a pointer to float. Yet we can pass an array of any length to a function expecting (in the form of a pointer) an array. So what can be done?

The real problem is that the size of the array is lost, so if we can find a mechanism for passing the size through to the function as well as the (pointer to the base of the) array, we'll be pretty content. Unsurprisingly, this is done via a template, which I've imaginatively called array_proxy (shown in Listing 14.3).

Listing 14.3.


template <typename T>


class array_proxy


{


// Construction


public:


  template <size_t N>


  explicit array_proxy(T (&t)[N])


    : m_base(t)


    , m_size(N)


  {}


  explicit array_proxy(T *base, size_t size)


    : m_base(t)


    , m_size(size)


  {}


// State


public:


  T *base()


  {


    return m_base;


  }


  size_t size() const


  {


    return m_size;


  }


// Members


private:


  T *const      m_base;


  size_t const  m_size;


// Not to be implemented


private:


  array_proxy &operator =(array_proxy const &);


};



There are also forwarding functions defined (which we see in action in section 14.6.5), that allow us to create an array_proxy instance without having to specify the type.



template <typename T, size_t N>


inline array_proxy<T> make_array_proxy(T (&t)[N])


{


  return array_proxy<T>(t);


}


template <typename T >


inline array_proxy<T> make_array_proxy(T *base, size_t size)


{


  return array_proxy<T>(base, size);


}



This template is featured throughout this chapter as it is refined to assist in some other array/pointer problems, so don't worry too much about its lack of polish for now.

We can now pass our array as an array:



void process_array(array_proxy<int> const &ar)


{


  printf("[");


  for(size_t i = 0; i < ar.size(); ++i)


  {


    printf("%d ", i[ar.base()]); // Just messing with ya!


  }


  printf("]\n");


}



Now this produces the desired result. Obviously, we'd like to have some nicer interface to the proxy than to have to specify base() in indexers. Hold that thought: more array_proxy coming up.


      Previous section   Next section