Question

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++?

Was it helpful?

Solution

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().)

OTHER TIPS

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.

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.

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top