74

Given the following program,

#include <iostream>

using namespace std;

void foo( char a[100] )
{
    cout << "foo() " << sizeof( a ) << endl;
}

int main()
{
    char bar[100] = { 0 };
    cout << "main() " << sizeof( bar ) << endl;
    foo( bar );
    return 0;
}

outputs

main() 100
foo() 4
  1. Why is the array passed as a pointer to the first element?
  2. Is it a heritage from C?
  3. What does the standard say?
  4. Why is the strict type-safety of C++ dropped?
einpoklum
  • 118,144
  • 57
  • 340
  • 684
CsTamas
  • 4,103
  • 5
  • 31
  • 34
  • I always use std::array in these cases, prevents having to deal with issues like this and works with std algorithms too – paulm Sep 08 '14 at 09:39
  • 3
    What strict type safety? Who has promised strict type safety? There is no such thing in C++. – n. m. could be an AI Sep 08 '14 at 09:58
  • TL;DR for below answers: Arrays become pointers when passed to the function, so when you check their size all you get is the size of a pointer. If you're working with just C, all I can suggest is that you pre calculate whatever size you're trying to get out of the array as another parameter. – Super Cat Dec 27 '15 at 22:04
  • [Relevant Linus rant](https://lkml.org/lkml/2015/9/3/428) – Millie Smith Dec 04 '17 at 21:51
  • Related: [determine size of array if passed to function](https://stackoverflow.com/questions/968001/determine-size-of-array-if-passed-to-function) – Gabriel Staples Sep 24 '20 at 21:17

3 Answers3

92

Yes it's inherited from C. The function:

void foo ( char a[100] );

Will have the parameter adjusted to be a pointer, and so becomes:

void foo ( char * a );

If you want that the array type is preserved, you should pass in a reference to the array:

void foo ( char (&a)[100] );

C++ '03 8.3.5/3:

...The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type "array of T" or "function returning T" is adjusted to be "pointer to T" or "pointer to function returning T," respectively....

To explain the syntax:

Check for "right-left" rule in google; I found one description of it here.

It would be applied to this example approximately as follows:

void foo (char (&a)[100]);

Start at identifier 'a'

'a' is a

Move right - we find a ) so we reverse direction looking for the (. As we move left we pass &

'a' is a reference

After the & we reach the opening ( so we reverse again and look right. We now see [100]

'a' is a reference to an array of 100

And we reverse direction again until we reach char:

'a' is a reference to an array of 100 chars

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • 1
    The latter has the disadvantage of hardwiring the array size into the function signature. A function template can avoid that. – sbi Aug 25 '09 at 13:29
  • On a somewhat related note, could someone clear up the syntax of the above for me? I'm obviously missing something, but I don't quite see how that evaluates to a reference to an array; it looks more like an array of references. – suszterpatt Aug 25 '09 at 13:40
  • 4
    it's probably worth mentioning that using a std::vector will neatly side step all of the problems related to passing arrays. – markh44 Aug 25 '09 at 14:02
  • 5
    This boils down to the fact that plain array parameters in C/C++ are a fiction - they're really pointers. Array parameters should be avoided as much as possible - they really just confuse matters. – Michael Burr Aug 25 '09 at 17:04
  • 2
    Just a nit-pick: the function parameter doesn't decay to pointer. It is *adjusted* to pointer. An array name used as a function *argument* can *decay* to a pointer if the function parameter is a pointer. – juanchopanza Apr 06 '15 at 14:50
  • 2
    Bravo for explaining the rule, since the link is now a 404. – gsamaras Apr 08 '15 at 13:43
  • @G.Samaras Nice catch. I've fixed it now. The appropriate section is 2.4. – Richard Corden Apr 08 '15 at 13:53
  • 1
    @RichardCorden the link now works. However, I liked so much your explanation that I do not feel I need to read the link at the time. ;p Thanks for the great answer. – gsamaras Apr 08 '15 at 14:38
16

Yes. In C and C++ you cannot pass arrays to functions. That's just the way it is.

Why are you doing plain arrays anyway? Have you looked at boost/std::tr1::array/std::array or std::vector?

Note that you can, however, pass a reference to an array of arbitrary length to a function template. Off the top of my head:

template< std::size_t N >
void f(char (&arr)[N])
{
  std::cout << sizeof(arr) << '\n';
}
sbi
  • 219,715
  • 46
  • 258
  • 445
  • 7
    But you can pass in "reference to array". – Richard Corden Aug 25 '09 at 13:24
  • @Richard: I was just adding this while you wrote your comment. `:)` – sbi Aug 25 '09 at 13:28
  • 7
    Passing by reference to array is not limited to "function templates". You can pass an array by reference to non template functions. The advantage of using a function template is that you can deduce the array index, thereby allowing that you can call the function for different sized array types. – Richard Corden Aug 25 '09 at 13:29
  • If I'd put the array in a struct and pass it to the function, it'd report the same size. So the rules for passing C arrays and objects as parameters are different ? – CsTamas Aug 25 '09 at 13:31
  • 4
    @CsTamas, yes, the rules for passing arrays and objects are different in C. Structures are actually copied by value when passed as a parameter. Arrays are treated as a pointer to their first element. (Arrays and pointers in C are very inter-related. They aren't the same thing, but for purposes of parameter-passing they are identical) – Tyler McHenry Aug 25 '09 at 13:34
  • @CsTomas: In 8.3.5/3 the standard describes the rules that are applied to parameters when a function is declared. These rules decide if the function is a redeclaration or an overload. This section explicitly covers where the parameter type is an array (or function) and this is why the behaviour differs between the array and structure case. – Richard Corden Aug 25 '09 at 13:37
  • @Richard: I know. (Also see my comment to http://stackoverflow.com/questions/1328223/sizeof-array-passed-as-parameter/1328246#1328246.) However, since the question was about passing array sizes, I suppose CsTamas wants to pass arrays of arbitrary length. I have edited my answer to emphasize that. – sbi Aug 25 '09 at 14:05
  • 1
    There is std::array now. – Trevor Hickey Apr 21 '16 at 05:47
  • @Trevor Thanks, added. – sbi Apr 21 '16 at 07:02
1

There is magnificent word in C/C++ terminology that is used for static arrays and function pointers - decay. Consider the following code:

int intArray[] = {1, 3, 5, 7, 11}; // static array of 5 ints
//...
void f(int a[]) {
  // ...
}
// ...
f(intArray); // only pointer to the first array element is passed
int length = sizeof intArray/sizeof(int); // calculate intArray elements quantity (equals 5)
int ptrToIntSize = sizeof(*intArray); // calculate int * size on your system
nickolay
  • 3,643
  • 3
  • 32
  • 40
  • 4
    And? This, at best, indirectly indicates _what_ happens. The OP was asking _why_ the language is set up that way. Also, "static array" is a confusing term when what you really mean is dynamically allocated; technically, the array you've shown has `extern` linkage, not `static`. And I'm not sure what function pointers have to do with anything here? – underscore_d Aug 30 '16 at 15:21