views:

1200

answers:

5

When compiling the following code:

void DoSomething(int Numbers[])
{
    int SomeArray[] = Numbers;
}

the VS2005 compiler complains with the error C2440: 'initializing' : cannot convert from 'int []' to 'int []'

I understand that really it's trying to cast a pointer to an array which is not going to work. But how do you explain the error to someone learning C++?

+3  A: 

Perhaps your answer could be, "Because the compiler doesn't know how big the array is."

Your example could work if there were explicit array sizes (perhaps with a typedef for clarity), and then you can explain pointers while introducing variable size allocations.

Greg Hewgill
That's only half of it. In C/C++, parameter types that look like array types are (confusingly) really pointer types -- the compiler will ignore any size given between [] on the top line, and still complain that you're trying to assign an address to an array even if you provide a size for the array.
j_random_hacker
+9  A: 

Say that there are types and incomplete types:

struct A;

Is an incomplete type of a struct called A. While

struct A { };

Is a complete type of a struct called A. The size of the first is not yet known, while the size of the second is known.

There are incomplete class types like the above struct. But there are also incomplete array types:

typedef int A[];

That is an incomplete array type called A. Its size is not yet known. You cannot create an array out of it, because the compiler does not know how big the array is. But you can use it to create an array, only if you initialize it straight away:

A SomeArray = { 1, 2, 3 };

Now, the compiler knows the array is an int array with 3 elements. If you try to initialize the array with a pointer, the compiler will not be any more clever than before, and refuse, because that won't give it the size of the array to be created.

Johannes Schaub - litb
-1 sorry. All true, but I think this answer may be too abstract to be useful to the asker. My guess based on his example code is that he is probably at the level of thinking that arrays are passed by value to functions receiving an int[] parameter.
j_random_hacker
I mean the person the asker is trying to help... :)
j_random_hacker
i think there are two issues. one is the initialization of the array with the pointer, and another is why it says "cannot convert from T to T". you are right i'm explaining only a small part of this. it's more difficult to write good long answers so i kept it short, because i would have messed up :)
Johannes Schaub - litb
+4  A: 

When I'm trying to explain something, I always try to go down to the lowest level, and build up from there. That's they way I like to learn things, and I find that people are more comfortable if you start with the basics which they know, and build up from there.

In this case, I'd probably start with something like:

The compiler is trying to do the assignment, because you wrote an assignment operation. In C++, you can't directly assign to an array, because it has no built-in assignment operator (of any kind, only initializer and indexing is supported for arrays). Because C++ supports overloaded operators for types, the compiler then looks for an overloaded assignment operator for the "assigned-to" type which takes the "assigned-from" type as its argument. Since there's also no overloaded operator for int[] which takes int[] as an argument, the compiler errors on the line, and the errors is telling you why the compiler can't process the line.

Yes, it's probably overkill vs just saying something about knowledge of size, incomplete types, etc. I realize it's also not complete (eg: no discussion of initializer assignment vs normal assignment, etc.). However, my goal is usually to get people to where they can figure out the next answer themselves, and for that you usually want to lay out the thought process for arriving at the answer.

Nick
I like this answer because it really explains the error wording
Gorpik
Yes, this is a good way to approach it. It's a shame that compiler's error message in this case is really unhelpful...
j_random_hacker
+7  A: 

In trying to make the error message more helpful, the compiler is actually confusing things. Even though the Numbers parameter is declared as an array, C/C++ do not (cannot) actually pass an array - the Numbers parameter is actually a pointer.

So the error really should say "cannot convert from 'int *' to 'int []'"

But then there would be confusion - "hey, there's no int* involved in the expression", someone might say.

For this reason it really is better to avoid array parameters - declare them as pointers, since that's what you're really getting anyway. And the explanation to someone learning C/C++ should educate them on the fact that array parameters are a fiction - they're really pointers.

Michael Burr
you are actually putting it very well :) i tried to formulate the business about the array parameter equally straight, but then limited myself at explaining the stuff about the initialization. anyway u get a +1 :p
Johannes Schaub - litb
Nice, this is a good way for the dialogue to develop.
j_random_hacker
+4  A: 

There are three things you need to explain to the person you're trying to help:

  1. Arrays can't be passed by value to a function in C++. To do what you are trying to do, you need to pass the address of the start of the array to DoSomething(), as well as the size of the array in a separate int (well, size_t, but I wouldn't bother saying that) argument. You can get the address of the start of some array myArray with the expression &(myArray[0]). Since this is such a common thing to want to do, C++ lets you use just the name of the array -- e.g. myArray -- to get the address of its first element. (Which can be helpful or confusing, depending on which way you look at it.) To make things even more confusing, C++ allows you to specify an array type (e.g. int Numbers[]) as a parameter to a function, but secretly it treats that parameter as though it was a declared as a pointer (int *Numbers in this case) -- you can even do Numbers += 5 inside DoSomething() to make it point to an array starting at the sixth position!

  2. When you declare an array variable such as SomeArray in C++, you must either provide an explicit size or an "initialiser list", which is a comma-separated list of values between braces. It's not possible for the compiler to infer the size of the array based on another array that you are trying to initialise it with, because...

  3. You can't copy one array into another, or initialise one array from another in C++. So even if the parameter Numbers was really an array (say of size 1000) and not a pointer, and you specified the size of SomeArray (again as say 1000), the line int SomeArray[1000] = Numbers; would be illegal.


To do what you want to do in DoSomething(), first ask yourself:

  1. Do I need to change any of the values in Numbers?
  2. If so, do I want to prevent the caller from seeing those changes?

If the answer to either question is "No", you don't in fact need to make a copy of Numbers in the first place -- just use it as is, and forget about making a separate SomeArray array.

If the answer to both questions is "Yes", you will need to make a copy of Numbers in SomeArray and work on that instead. In this case, you should really make SomeArray a C++ vector<int> instead of another array, as this really simplifies things. (Explain the benefits of vectors over manual dynamic memory allocation, including the facts that they can be initialised from other arrays or vectors, and they will call element constructors when necessary, unlike a C-style memcpy().)

j_random_hacker