8

I am confused about the output of the following code.

#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
  int a[] = {1,2,3};
  cout << a         << "  " << &a         << endl;
  cout << sizeof(a) << "  " << sizeof(&a) << endl;
  return 0;
}

The output is

0xbfcd3ae4  0xbfcd3ae4
12  4

How can a and &a print the same expression but have different sizes? I always thought that for any array, its name always has the value = address of the first byte.

Also &a should not make sense, since one cannot have an address (obtained with the & operator) to an address(the value of a). Yet the code gives an output and infact 'a == &a' according to the output.

Similarly why is the output of sizeof(a) = 12 (which is the total memory occupied) by the array? a being a "pointer" itself sizeof(a) = 4 bytes (on my 32 bit Ubuntu 11.04)

Obviously there is some misconception I am having. Could some one sort this out for me ?

Deanie
  • 2,316
  • 2
  • 19
  • 35
curiousexplorer
  • 1,217
  • 1
  • 17
  • 24
  • 6
    `sizeof(a)` is `sizeof(int[3])`, `sizeof(&a)` is `sizeof(int*)`. – user2864740 Jan 03 '14 at 20:45
  • 1
    Your second paragraph, `&a` is type `int (*)[3]`, and yes it makes sense. Consider `sizeof(*(&a))` against `sizeof(a)`. They *better* be the same, or pointer math just went out the window. – WhozCraig Jan 03 '14 at 20:45
  • Taking the address of an address makes perfect sense. The address has to live somewhere in memory, doesn't it? – Linuxios Jan 03 '14 at 20:51
  • @Linuxios Taking the address of a *pointer* variable makes crystal-clear sense; as they both hold, and *have*, and address. Arrays a different (and this difference is the fundamental divergence between pointers and arrays). Pointers are variables that *hold* an address (and have one of their own). Arrays are variables that *are* an address. Both their value and their address via `&` are equivalent, and though the *values* are the same (a memory address), the types, given `a` vs `&a`, are *not*. The related standard citation regarding the value equivalence is in C99 §6.5.3.2. – WhozCraig Jan 03 '14 at 21:05
  • 1
    [Near duplicate](http://stackoverflow.com/q/2528318/179910). – Jerry Coffin Jan 03 '14 at 21:15
  • 4
    Don't use the output of streaming to `cout` as proof for anything unless you understand the various overloads it has to provide various human-readable representations of things. – Lightness Races in Orbit Jan 03 '14 at 21:49
  • 3
    @user2864740 `sizeof(&a)` is not `sizeof(int*)` but `sizeof(int(*)[3])` (which happen to be the same). Subtle but important difference! – tab Jan 03 '14 at 21:58
  • This question is different from the alleged duplicate in that it is asking about the address of an array vs the array, whereas the duplicate is asking about the array vs. the address of its initial element. Voting to reopen. – Sergey Kalinichenko May 17 '14 at 12:16
  • @LightnessRacesinOrbit What else method shall I use to test something like this then? – Rick Apr 08 '18 at 15:12

4 Answers4

14

An array is not a pointer, but an array decays to a pointer when you try to use it like one. In your case printing the address of the array automatically converts it into a pointer.

There's little difference between the automatically converted pointer and the one created explicitly with &, except that one is a pointer to a single element while the other is a pointer to the entire array. If you had used &a[0] then they would be identical.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • ... where almost everything counts as "trying to use it as a pointer". Only `sizeof`, taking the address and thinks like `typeid` *don't* immediately turn it into a pointer. The last paragraph seems wrong though: Either you mean `&a[0]` by "the one created explicitly", or there *is* a difference - the type! –  Jan 03 '14 at 20:48
  • @delnan of course you are correct. I'm guilty of over-simplification. You can see the proof here: http://ideone.com/ZvwfpS – Mark Ransom Jan 03 '14 at 20:53
7

First of all, realize that there is a difference between an object1 and the expression that we use to refer to that object. In your code, a is an expression that refers to an object large enough to store 3 int values.

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

Given a statement like

cout << a;

the expression a has type "3-element array of int"; since a is not an operand of the sizeof or unary & operators, it will be converted to an expression of type "pointer to int", and the value of the expression will be the address of the first element in the array.

OTOH, given a statement like

cout << &a;

the expression a is the operand of the unary & operator, so the rule doesn't apply; instead, the type of the expression is "pointer to 3-element array of int", and the value of the expression is the address of the array.

In both C and C++, the address of the array and the address of the first element of the array are the same, so both expressions yield the same value, but the types of the two expressions are different (int * vs. int (*)[3]).

In the statement

cout << sizeof a; // or sizeof (a)

the expression a is the operand of the sizeof operator, so again, the conversion rule doesn't apply; instead, the expression sizeof a evaluates to the number of bytes used by the array (in this case, 12).

In the statement

cout << sizeof &a; // or sizeof (&a)

the expression &a evaluates to the address of the array and has type int (*)[3], so sizeof &a evaluates to the number of bytes used by the pointer type (in your case, 4 bytes).

In both C and C++, when you declare an array like

int a[3];

the only storage set aside is for the 3 array elements; there's no separate storage for an a variable that points to the first element of the array (which is why a and &a yield the same value).


1. In the sense of something that occupies memory, not the sense of an instance of a class
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Thanks for the reply. With regards to the last point, this would be a difference between `int* a = new int[3]` with intializations and `int a[] = {1,2,3}`. In the former case we are creating an object a to hold the address of the first element whereas in the later `a` refers to the entire block of 3 elements when using the sizeof operator – curiousexplorer Jan 03 '14 at 22:09
  • @curiousexplorer: yes. exactly. – John Bode Jan 06 '14 at 17:16
  • @curiousexplorer both cases feature an array of 3 ints. In the first one, that array does not have a designator; and there is a separate variable `a` which points to the first element of the array. In the second example, the array does have a designator `a` and there is no pointer. – M.M May 17 '14 at 12:22
1

In C, the expression a is a primary expression which designates an array object. In a context where this expression is evaluated, it produces a value which is of pointer type, and points to the first element of the array.

This is a special evaluation rule for arrays. It does not mean that a is a pointer. It isn't; a is an array object.

When the sizeof of & operators are applied to a, it is not evaluated. sizeof produces the size of the array, and & takes its address: it produces a pointer to the array as a whole.

The values of the expressions a and &a point to the same location, but have a different type: a pointer to type of the array element (such as pointer to int), versus the type of the array (such pointer to array of 10 int) respectively.

C++ works very similarly in this area for compatibility with C.

There are other situations in C where the value of an expression which denotes a value or object of one type produces a value of another type. If c has type char, then the value of c is a value of type int. Yet &c has type char *, and sizeof c produces 1. Just like an array is not a pointer, c is not an int; it just produces one.

C++ isn't compatible in this area; character expressions like names of char variables or character constants like 'x' have type char. This allows void foo(int) and void foo(char) to be different overloads of a function foo. foo(3) will call the first one, and foo('x') the second.

Kaz
  • 55,781
  • 9
  • 100
  • 149
1

Look at this following code for better understanding:

#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
  int a[] = {1,2,3};
  cout << a         << "  " << &a         << endl;
  cout << a+1         << "  " << &a+1         << endl;
  cout << sizeof(a) << "  " << sizeof(&a) << endl;
  return 0;
}

Output:

0x7fffb231ced0  0x7fffb231ced0
0x7fffb231ced4  0x7fffb231cedc
12  8

We got same address for a and &a. So we you may think they are same. But that not true, because a variable (a) and its address (&a) cannot be same.

Both printed address, but both are printing different addresses, "a” is a “pointer to the first element of array” but “&array” is a “pointer to whole array”.

That's why when we printed a+1 we got 0x7fffb231ced4which is address to second element in array (and since it array of integers, its increased by 4) while &a+1 got us 0x7fffb231cedc which is increment of 4*3= 12 (c means 12 in hex) because we had 3 elements in array. &arr+1 points to next memory location after array.

Also this fundamental difference is what results in different in sizes of a and &a. size of a is actually the size of array while size of &a is size of pointer.

Suryansh Singh
  • 1,123
  • 7
  • 15