0

When I was programming in C++ today I noticed this phenomenon:

int main()
{
    int a = 42;
    func (a);

    cout << a << endl;
}

void func (int x)
{
    x = 5;
}

And of course the output was 42. Otherwise to get 5 as output i can rewrite the function using the address:

void func (int &x)
{
    x = 5;
}

This is what I understand, but when I did this:

int main()
{
    int a[2][2] = { {2,2}, {2,2} };
    func (a);

    cout << a[1][2] << endl;
}

void func (int x[2][2])
{
    x[1][2] = 5;
}

The output actually was 5, but for me this is unexplainable. Why should func affect any variables of main? Just because it's a 2d-Array?

Jojo
  • 41
  • 1
  • 6
  • You shouldn't have used arrays in the first place. – n. m. could be an AI Nov 03 '15 at 16:31
  • Why the mark down? Question looks perfectly reasonable to me. – Component 10 Nov 03 '15 at 16:32
  • "Otherwise to get 5 as output i can rewrite the function using the address". Careful: `void func (int &x)` means you are passing by *reference*. `int &` is a reference to an int. Sure, it looks like the "address of" operator, but it's different. When you are *calling* the function, `&` means you want to pass the address of the variable (for example if you called `func(&a);`, but in function *declarations* and *definitions* you are indicating that the argument will be passed by reference. Maybe you have just used the wrong word, but in case this concept is not clear, definitely check it out! – Fabio says Reinstate Monica Nov 03 '15 at 16:40
  • I see Christian Hackl has just explained what I meant. – Fabio says Reinstate Monica Nov 03 '15 at 16:46
  • @FabioTurati: The (understandably) confusing thing for C++ beginners is just that the `&` character is used for two completely unrelated things. – Christian Hackl Nov 03 '15 at 16:48
  • @ChristianHackl: Yes! I so wish Stroustrup had chosen another way to designate references (for example `$` or `#`)... It would have avoided some confusion. But I'm afraid it's a little too late to change it now! :-) – Fabio says Reinstate Monica Nov 03 '15 at 16:58

3 Answers3

3

Otherwise to get 5 as output i can rewrite the function using the address:

void func (int &x)
{
    x = 5;
}

There is no "address" here. x is a reference. Don't confuse references and pointers; they are quite different things.

void func (int x[2][2])

This is an attempt at passing an array to a function. What happens really is that you pass a pointer. The array which you pass is said to "decay" to a pointer to the array's first element (losing all size information).

It's as if you had written your function with a lovely parameter type like this:

void func(int (*x)[2])
{
    x[1][2] = 5;
}

What's confusing here is the fact that you've used a 2-dimensional array. The second dimension's size is actually preserved in the type. You could pass x[1] again to a function like void func2(int y[2]), and it would decay to a simple int*.

Nevertheless, the point is that you have not passed a reference but a pointer. The pointer itself is passed by value - but the pointer is used to indirectly modify the array in main.

Note that it is possible to pass arrays by reference. Here's how:

void func(int (&x)[2][2])

Conclusion:

  • You need to understand the difference between references and pointers.
  • An attempt to pass an array by value makes it "decay" to a pointer to its first element.
  • It is possible to pass arrays by reference, even though the syntax is ugly.
  • Two-dimensional arrays serve only to make the first three rules appear more complicated than they really are.
  • Use std::vector / std::array instead of raw arrays.
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
2

First of all this function declaration

void func (int x[2][2])
{
    x[1][2] = 5;
}

is equivalent to

void func (int x[][2])
{
    x[1][2] = 5;
}

and in turn is equivalent to

void func (int ( *x )[2])
{
    x[1][2] = 5;
}

that is the parameter has type of pointer to one-dimensional array of type int[2]. The function has gotten an address of a memory.

When you call the function the following way

func (a);

array a is implicitly converted to pointer to its first "row" (first element).

So the function deals with a pointer. It does not change the pointer itself. It changes thye memory pointed to by this pointer.

Within the function body expression expression

x[1]

is equivalent to *( a + 1 ) and yields the second "row" of the array a (indices starts from 0).Let's name it row

Expression

x[1][2]

is equivalent to row[2] and yields reference to the third element of this row.

The value in this cell of the memory occupied by the array is changed in the function. That is the function does not deal with a copy of the value in this cell. It deals directly with the cell itself because we provided its address.

Take into acccount that this statement

cout << a[1][2] << endl;

is wrong. The valid range of indices for array a declared like

int a[2][2]

is [0, 1].

So you are trying to override memory beyond the array.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

This is called Decay.
Arrays decay into raw pointers when passed as parameters.

for the compiler, the decleration

void func (int x[2][2]); 

is the same as

void func (int* x[2]);

thus the array is sent as a pointer to the first element and the function can change the original array.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • For a good learning effect, the OP should try to define both functions. The compiler will complain about a function redefinition. – Christian Hackl Nov 03 '15 at 16:44
  • @ChristianHackl i tried both of the functions given in this answer, and the compiler does not complain. Why is that? Am I doing something wrong or does my compiler have a bug? – Johannes Schaub - litb Nov 03 '15 at 18:17
  • @ᐅJohannesSchaub-litbᐊ: Ah, I see only now that the inner parentheses are missing in the second function. Well... C declaration syntax is hard :) – Christian Hackl Nov 03 '15 at 19:43
  • @ᐅJohannesSchaub-litbᐊ: For anyone else reading and wondering, I was referring to something like `void func (int x[2][2]) {} void func (int (* x)[2]) {}` – Christian Hackl Nov 03 '15 at 19:44