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!

5
  • 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) Commented Dec 29, 2021 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<void const*>(&a). The reason is that it makes it clearer what's going on and further it avoids the special cases for character pointers. Commented Dec 29, 2021 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. Commented Dec 29, 2021 at 11:24
  • ...And then remember that c++ offers std::array and std::vector on top of legacy c arrays Commented Dec 29, 2021 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. Commented Dec 29, 2021 at 12:23

7 Answers 7

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.

Sign up to request clarification or add additional context in comments.

5 Comments

This function copies the array to its local variable, right?
@digito_evo The array is not copied. Just a pointer to the same array's first element is passed.
@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 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 - 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 structs to the language, the array to pointer decay became less "obvious", but apparently too late to change.
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

Comments

2

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

Comments

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.

Comments

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;
}

Comments

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]

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.