![]() | |
![]() ![]() |
![]() | 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 FunctionsWhat 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:
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. ![]() |
![]() | |
![]() ![]() |