Array as a parameter

Is there a way to find the length of an array that is passed as a parameter?
If you are talking about a C-array and from inside the function, the answers no. You should pass the length as a separate parameter.

Andy

PS The std::array in C++11 (formerly known as C++0x), along with its precursor the Boost array, is a different matter
Last edited on
If you're using Visual C++ 2005 or later (and maybe VC++.Net?) you have _countof() available, which evaluates to the usual trick for C code

#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))

but this rather tortuous version for C++ (which stops you from accidentlly using it on a pointer)

1
2
3
template <typename _CountofType, size_t _SizeOfArray>
char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
#define _countof(_Array) (sizeof(*__countof_helper(_Array)) + 0) 


Which is obviously a relative of the SizeOfArray function Duoas provided a link to.

1
2
3
4
5
6
template <typename T, size_t N>
inline
size_t SizeOfArray( const T(&)[ N ] )
{
  return N;
}


Not sure why Microsoft went for the templated function + macro approach.

(these snippets ard from the VC++ 2010 Express version of stdlib.h)

Andy

PS UNALIGNED is defined to nothing when compiling for x86. For x64 and Itanium, it ends up as __unaligned, to stop __countof() choking on unaligned data. From MSDN entry of __unaligned

The Itanium processor generates an alignment fault when it accesses misaligned data, and the time to process the fault weakens performance. Use the __unaligned modifier to cause the processor to read data one byte at a time and avoid the fault.
Last edited on
I haven't learned templates yet. So for the time being, I will assume it is not possible.
Even with the SizeOfArray (or _countof, etc) you have to use a second param.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<iostream>
using namespace std;

// Borrowed from Duoas' article
// http://www.cplusplus.com/faq/sequences/arrays/sizeof-array/#cpp
template <typename T, size_t N>
inline
size_t SizeOfArray( const T(&)[ N ] )
{
    return N;
}

// As msg_array has no size here, SizeOfArray() does not work for
// it so have to pass array length explicitly, as another param
void print_msg(const char msg_array[], size_t array_size)
{
    for (size_t index = 0; array_size > index; ++index)
    {
        cout << msg_array[index];
    }
}

int main()
{
    const char hello_array[] // no null terminator
    = { 'H', 'e', 'l', 'l', 'o', ',', ' ',
        'w', 'o', 'r', 'l', 'd', '!', '\n' };

    // Even though there's no size in the [], hello_array knows how
    // big it's supposed to be (or rather, the compiler does) because
    // of the chars used to fill it. So SizeOfArray() works
    print_msg(hello_array, SizeOfArray(hello_array));
    // VC++ programmers could use _countof() instead of SizeOfArray()
    // but not sure about GCC, etc

    return 0;
}
Last edited on
If you will declare the parameter as a reference to array then there is no problem. For example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

void f( int ( &a )[10] )
{
   std::cout << "Dimension of a is " << sizeof( a ) / sizeof( *a ) << std::endl;
}


int main()
{
   int a[10];

   f( a );
}


But in this case you shall pass as argument an array that has the same number of elements as declared in the parameter.
Last edited on
Sorry for the confusion. I meant there was no way to do it without the parameter.

Thanks for the help though.
Er, andy, you don't have to use a second argument. (It is a really bad idea not to, but you don't have to.)

Instead, you can use something of a hybrid approach that keeps your executable small and lets you pretend that you don't care how long your array is. We do it using an inline thunk trick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<iostream>
using namespace std;

// Here is the work function. It takes an array of ANY size.
// You have to explicitly give its size, though.
void print_msg(const char msg_array[], size_t array_size)
{
    for (size_t index = 0; array_size > index; ++index)
    {
        cout << msg_array[index];
    }
}

// Here is our compiler magic. It lets us use the above function
// as if it just took an array of any size WITHOUT having to 
// explicitly give it. Of course, you could just call the above
// function directly, but this makes so that you don't have to.
//
// If your compiler's optimizer works properly, this function
// should actually disappear from the compiled code.
//
template <size_t N>
inline
void print_msg( const char(& msg_array)[ N ] )
{
    print_msg(msg_array, N);
}

int main()
{
    const char hello_array[] // no null terminator
    = { 'H', 'e', 'l', 'l', 'o', ',', ' ',
        'w', 'o', 'r', 'l', 'd', '!', '\n' };

    // Now I can use hello_array without caring how long it is.
    // All the messy details are handled above.
    print_msg(hello_array);

    // This would still work also, if SizeOfArray() were still defined above: 
    //print_msg(hello_array, SizeOfArray(hello_array));
    // Likewise:
    //print_msg(hello_array, 14);

    return 0;
}

Now it works either way.

This trick works because the templated "thunk" function is small and should be inlined, or eliminated from, the actual compiled code.

If you were to instead write just one templated function that actually does all the work, then the compiler will generate a new function for every different array you give it. That would be wasteful (or "code bloat"). [AFAIK there are no optimizers smart enough to recognize this kind of situation.]

Hope this helps and enlightens. :O)
:-)

Well, to be honest I think I'll stick to the C-array and blatant second parameter.

I guess, at the end of the day, I believe you should either:
1. use a C-array with an extra param, or
2. use a C++ vectror/array class

Andy





Last edited on
I agree, but I think you missed my point.

The point was that the user of your function should be able to use the C-array with the same syntax as the C++ container class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <vector>

template <typename Iterator>
void print( Iterator begin, Iterator end )
  {
  while (begin != end)
    std::cout << *begin++ << std::endl;
  }

template <typename Container> inline
void print( const Container& seq ) { print( seq.begin(), seq.end() ); }

template <typename T, size_t N>
void print( const T(& seq)[ N ] ) { print( seq, seq + N ); }

int main()
  {
  using namespace std;
  const char* xs[] = { "two", "three", "five", "seven", "eleven" };
  vector <string> ys( xs, xs + 5 );

  print( xs );  // Do I care which is an array and which is a container?
  print( ys );  // No, let the compiler care.

  return 0;
  }


This is actually implemented helpfully in C++11 by providing begin() and end() functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

template <typename Iterator>
void print( Iterator begin, Iterator end )
  {
  while (begin != end)
    std::cout << *begin++ << std::endl;
  }

int main()
  {
  using namespace std;
  const char* xs[] = { "two", "three", "five", "seven", "eleven" };
  vector <string> ys( xs, xs + 5 );

  print( begin( xs ), end( xs ) );  // Do I care which is an array and which is a container?
  print( begin( ys ), end( ys ) );  // No, let the compiler care.

  return 0;
  }

:^]

Oops! Fixed typo.
Last edited on
Taking templates out of consideration, the answer is no besides passing it as a parameter.
Correct.
Topic archived. No new replies allowed.