2

I'm sorry for my bad English first. I've encountered a strange problem when coding in C++.

using namespace std;

void Func(int a[2][3])
{
   cout <<(int) &a;
}

int main()
{
   int a[2][3] =
   {
       {1,2,3},
       {4,5,6}
   };
   cout << (int)&a << endl;
   Func(a);
   return 0;
}

I was confused that &a in main() and in function Func() returned different values. And strangely, the difference between them always is 212. Can anyone explain please? Thank you for your help.

P/s:Thank you all for your answer .My teacher says that C++ doesn't allow passing an array by value, because if the array has 1 million elements, that would decrease the performance a lot only for copying all of them, so he says only pass by reference is allowed. That's what make me think those two &a should be the same. Now I get it, thank you everyone!

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 7
    Why did you expect them to have the same value? You pass `a` by value (even if it decays to a pointer, that pointer is still passed by value) – UnholySheep Dec 29 '21 at 11:18
  • 1
    BTW: There's no need for the cast to int and it's even harmful, because an int may not be able to represent an address. Instead, write the pointer to the stream directly. The only thing I'd do is to `cout << static_cast(&a)`. The reason is that it makes it clearer what's going on and further it avoids the special cases for character pointers. – Ulrich Eckhardt Dec 29 '21 at 11:23
  • 2
    Would it be clearer if you named one of the two variables `b`? Repeating the same variable name may be reinforcing the incorrect notion that they are the same variable. – Drew Dormann Dec 29 '21 at 11:24
  • ...And then remember that c++ offers `std::array` and `std::vector` on top of legacy c arrays – MatG Dec 29 '21 at 11:54
  • 1
    Look at the *value* of the argument `a` (`cout <<(int) a`) instead of its location. Also, look at `&a[0]` in `main`. – molbdnilo Dec 29 '21 at 12:23

7 Answers7

3

Your function declaration

void Func(int a[2][3])

is completely equivalent and interchangeable with:

void Func(int (*a)[3]).

As you can see, you are passing a pointer to an array of three ints by value. Therefore the address of the local function parameter is different from the address of the variable in main, even if they may hold the same value.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • This function copies the array to its local variable, right? – digito_evo Dec 29 '21 at 11:25
  • @digito_evo The array is not copied. Just a pointer to the same array's first element is passed. – user17732522 Dec 29 '21 at 11:36
  • @digito_evo - No, it passes a pointer to the array, not a copy. The fact that you can declare the parameter as an array and still get a pointer, is a mistake made in C in the early 1970s. :-) – BoP Dec 29 '21 at 11:37
  • @BoP Wow that's interesting. I still don't quite understand how it works. So it was a design flaw in C. I didn't know about this. – digito_evo Dec 29 '21 at 12:09
  • 1
    @digito_evo - It might have been "obvious" at the time. In early C you could only pass simple types, and only by value. So when you tried to pass an array, the compiler "helped" you by making it a pointer and pass that by value. Because that must have been what you meant, obviously. Only later, when adding `struct`s to the language, the array to pointer decay became less "obvious", but apparently too late to change. – BoP Dec 29 '21 at 12:18
2

You're passing the a argument by value, so each function has its own copy of it (of the pointer, not the array data). The constant offset you're seeing comes from the distance between the stack frames of the two functions, and this is constant.

If you change the function to get a reference to the array (void Func(int (&a)[2][3]) you will get the same value in both cases

Dario Petrillo
  • 980
  • 7
  • 15
2

The parameter and the local variable are distinct objects and since their lifetimes overlap, they must have distinct memory addresses.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

Please read about pass by value and pass by references. So what happened here is:

  1. you initialised an array in main function. &a will refer to the address of a.
  2. you passed as a pass by value argument to another function. A copy of a is created to be consumed in Func and &a will refer to the memory location of a local to Func.

I hope the concept is clear.

Abhash Kumar Singh
  • 1,157
  • 2
  • 12
  • 22
1

Use the following syntax to pass arrays by (const) reference : const int (&a)[2][3]

#include <iostream>

void func(const int (&a)[2][3])
{
    for (const auto& row : a)
    {
        for(const auto& value : row )
        {
            std::cout << value << " ";
        }
        std::cout << "\n";
    }
}

int main()
{
    int a[2][3] =
    {
        {1,2,3},
        {4,5,6}
    };

    func(a);

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
1

This is because C rules on how pointers and arrays work are a little weird. You're actually taking the address of a pointer to the array, not the actual address of the array. If you want to get the address to the array you need to take the address of the first element instead:

&a[0]
Cubic
  • 14,902
  • 5
  • 47
  • 92
0

For starters it is a bad idea to cast an address to the type int like

cout << (int)&a << endl;

you could just write

cout << static_cast<void *>( a ) << endl;

or

cout << static_cast<void *>( &a ) << endl;

Or even like

cout << a << endl;
cout << &a << endl;

though with static_cast the code looks more readable.

The both statements will output the same value: the address of the extent of memory occupied by the array.

In this function call

Func(a);

the array designator is implicitly converted to pointer to its first element of the type int( * )[3].

The value of the pointer expression is assigned to the local variable (parameter) a of the function Func.

The function parameter of the pointer type a and the array a defined in main occupy different extents of memory.

If to rename the function parameter as for example

void Func(int b[2][3])

to distinguish it from the array with the same name defined in main then you may imagine the function call the following way

Func(a);

//...

void Func( /*int b[2][3] */ )
{
   int ( *b )[3] = a;
 
   cout << static_cast<void *>(  &b );
}

Pay attention to that the function parameter declared as having the array type int[2][3] is implicitly adjusted by the compiler to pointer to the array element type that is int ( * )[3].

So as you can see this statement

   cout << static_cast<void *>(  &b );

outputs the address of the local variable (parameter) b.

If you want to get the address of the array a within the function Func then you should write

   cout << static_cast<void *>(  b );

In this case the addresses outputted in main and in the function will coincide because the parameter b stores the address of the first element of the array a.

Here is a demonstration program.

#include <iostream>

void Func( int a[2][3] )
{
    std::cout << static_cast< void * >( a ) << '\n';
}

int main()
{
    int a[2][3] =
    {
        {1,2,3},
        {4,5,6}
    };

    std::cout << static_cast<void *>( a ) << '\n';
    std::cout << static_cast<void *>( &a ) << '\n';

    Func( a );
}

The program output might look like

010FFD08
010FFD08
010FFD08

As you can see the three values are equal each other.

But if you will write in the function Func

    std::cout << static_cast< void * >( &a ) << '\n';
                                       ^^^^

you will get the address of the local variable (parameter) a of the function. It is evident that this address differs from the address of the extent of memory occupied by the array because for the parameter a there was allocated a separate extent of memory the size of which is equal tp the value of the sizeof( int( * )[3] ) and usually this value is equal to 4 or 8.

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