34

I am having a tough time understanding the type and use of the name of the array in C. It might seems a long post but please bear with me.

I understand that the following statement declares a to be of type int [] i.e array of integers.

int a[30];

While a also points the first element of array and things like *(a+2) are valid. Thus, making a look like a pointer to an integer. But actually the types int [] and int* are different; while the former is an array type and later is a pointer to an integer.

Also a variable of type int [] gets converted into a variable of type int* when passing it to functions; as in C arrays are passed by reference (with the exception of the sizeof operator).

Here comes the point which makes me dangle. Have a look at the following piece of code:

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

    p = &a;

    printf("a:%d\t&a:%d\n",a,&a);
    printf("%d",*(*p + 2));
}

OUTPUT:

a:2686720       &a:2686720
6

So, how does the above code work? I have two questions:

  1. a and &a have the same values. Why?
  2. What exactly does int (*p)[3]; do? It declares a pointer to an array, I know this. But how is a pointer to an array different from the pointer to the first element of the array and name of the array?

Can anyone clarify things up? I am having a hell of a lot of confusions.

I know that I should use %p as a placeholder instead of using %d for printing the value of pointer variables. As using the integer placeholder might print truncated addresses. But I just want to keep things simple.

darxtrix
  • 2,032
  • 2
  • 23
  • 30
  • 2
    http://www.c-faq.com/ section 6. – Keith Thompson Jun 28 '14 at 14:30
  • A [possibly related answer](http://stackoverflow.com/a/12674201/596781). – Kerrek SB Jun 28 '14 at 14:31
  • Just to sum up what @ouah wrote in his excellent answer: `&p[0] == &a[0]` and `&p[1] == &a[3]` – ciamej Jun 28 '14 at 14:49
  • 5
    `sizeof` is not a *function*. It's an *operator*. The parens are not generally necessary. The reason they're needed for `sizeof(int)` is because `(int)` is a type. So, for example, if you have `int a[10]`, then you can use sizeof like `sizeof a`, without any parens. – ooga Jun 28 '14 at 14:53
  • @ooga, nice learning.Can you elaborate it more or add a handle to some related SO post. – darxtrix Jun 28 '14 at 14:54
  • 1
    You can search for SO posts if you want. If you're asking for proof that `sizeof` is not a function, see the [C99 Standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf), section 6.4.1, where it is listed as a *keyword*, and hence cannot be a function. See also section 6.5.3 (*unary operators*), and section 6.5.3.4 (*The sizeof operator*). – ooga Jun 28 '14 at 15:05
  • @ooga +1 for pointing out standards. – darxtrix Jun 28 '14 at 15:13
  • 2
    The type of `a` when you declare `int a[30]` isn't `int []`; it's `int [30]`. The array length is part of the type. – user2357112 Jun 28 '14 at 19:33
  • 2
    About `sizeof`, even without checking the standard, you should be able to realize that sizeof couldn't possibly be a function. It is impossible to implement a function that does what sizeof does: not only is the information needed not available to a C function, but the answer is needed at compile time. Only the compiler itself can provide the result of sizeof. – Euro Micelli Jun 29 '14 at 04:25
  • @EuroMicelli quite helpful – darxtrix Jun 29 '14 at 04:28

6 Answers6

52

Other answers already explained the issue. I am trying to explain it with some diagram. Hope this will help.


When you declare an array

int a[3] = {5, 4, 6}  

the memory arrangement looks like

enter image description here

Now answering your question:

  1. a and &a have the same values.How?

As you already know that a is of array type and array name a becomes a pointer to first element of array a (after decay),i.e it points to the address 0x100. Note that 0x100 also is the starting address of the memory block (array a). And you should know that, in general, the address of the first byte is said to be the address of the variable. That is, if a variable is of 100 bytes, then its address is equal to the address of its first byte.

&a is address of the entire memory block, i.e it is an address of array a. See the diagram:

enter image description here

Now you can understand why a and &a both have same address value although both are of different type.

What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?

See the above figure, it is explained clearly how pointer to an array is different from the pointer to an array element.
When you assign &a to p, then p points to the entire array having starting address 0x100.


NOTE: Regarding to the line

... as in C arrays are passed by references (with exception of sizeof function).

In C, arguments are passed by value. No pass by reference in C. When an ordinary variable is passed to a function, its value is copied; any changes to corresponding parameter do not affect the variable.
Arrays are also passed by value, but difference is that the array name decays to pointer to first element and this pointer assigned to the parameter (here, pointer value is copied) of the function; the array itself isn't copied.
In contrast to ordinary variable, an array used as an argument is not protected against any change, since no copy is made of the array itself, instead copy of pointer to first element is made.

You should also note that sizeof is not a function and array name does not act as an argument in this case. sizeof is an operator and array name serves as an operand. Same holds true when array name is an operand of the unary & operator.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • 2
    Curiously enough, I cannot find any 'legal' foundation for the statement that 'the address of the *first* byte is said to be the address of the variable'. Of course, I cannot think of a practical reason for an implementation designer to make it other way, but as far as I can see, the Standard does not forbid that. Therefore, from puristic point of view, `printf("a:%p\t&a:%p\n", reinterpret_cast(a), reinterpret_cast(&a));` *might* print two different values. – ach Jun 28 '14 at 22:36
  • @AndreyChernyakhovskiy; When you are dealing with arrays, then `printf("a:%p\t&a:%p\n", reinterpret_cast(a), reinterpret_cast(&a));` *will always print same address*. There is no possibility that they point to different location. And lets suppose, for a moment, that `a` and `&a` both points to different locations, `0x100` and `0x300` respectively. Now you have a function that expects a pointer to array, i.e `int (*)[]` type, let `p` be the parameter with type `int (*)[]`. In this case you have to pass `&a` as argument. To access the elements of the `a` in function,.. *continued*. – haccks Jun 29 '14 at 06:44
  • you must need base address of array, i.e `(*p)[i]` will give the `i`th element of array if and only if `(*p)` has the same address as that of `a`. But, as we assumed, `a` and `&a` doesn't point to same location, therefore compiler will never get the address of first element by the pointer arithmetic `*p + 1`, of course it will give `0x504`, and accessing unallocated momory can be disastrous. – haccks Jun 29 '14 at 06:50
  • 2
    @haccks, you haven't got my point. The standard never defines what is the address of an object. It is quite natural for implementation to define it as the address of the *first* byte of memory location reserved for the object. But suppose a misanthropic designer wishes to define it as the address of the *last* byte. Are there any clauses in the standard which would forbid that? As far as I can see, no. Of course, some type casts (including array type decay) will have to adjust the address, but if done accurately the defined semantics will be preserved. – ach Jun 29 '14 at 10:01
  • @AndreyChernyakhovskiy; *Are there any clauses in the standard which would forbid that?*: No. I got your point. But you can say it is a general convention. I added to my answer: *in general*, to avoid confusion. – haccks Jul 01 '14 at 17:46
  • @haccks Is there any examples show the use of pointer to an array? – Tony Jul 12 '14 at 08:05
  • @Tony; Pointer to an array often used as function parameter when you need to pass a 2D array to a function. In fact the function declaration `int foo(int n, int a[m][n]);` is treated by compiler as `int foo(int n, int (*a)[n]);`. – haccks Jul 12 '14 at 08:27
  • @haccks Does compiler do this for efficiency? – Tony Jul 12 '14 at 10:58
  • @Tony; There is a rule in C that array names is converted to pointer to first element when used as a function parameter/argument. – haccks Jul 12 '14 at 11:19
  • @haccks Thank you for your patience (though I still don't know why we need such pointer to an array.) – Tony Jul 13 '14 at 04:54
17
  1. a and &a have the same values.How?

They have the same value but different types. Array objects have no padding between elements (before or after) so the address of the array and the address of the first element of the array are the same.

That is:

(void *) a == (void *) &a
  1. What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?

These are two different pointer types. Take for example, pointer arithmetic:

a + 1   /* address of the second element of the array */
&a + 1  /* address one past the last element of the array */

EDIT: due to popular demand I added below some information about conversion of arrays.

With three exceptions, in an expression an object of type array of T is converted to a value of type pointer to T pointing to the first element of the array. The exceptions are if the object is the operand of sizeof or & unary operator or if the object is a string literal initializing an array.

For example this statement:

printf("a:%d\t&a:%d\n", a, &a);

is actually equivalent to:

printf("a:%d\t&a:%d\n", &a[0], &a);

Also please note that d conversion specifier can only be use to print a signed integer; to print a pointer value you have to use p specifier (and the argument must be void *). So to do things correctly use:

printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);

respectively:

printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
ouah
  • 142,963
  • 15
  • 272
  • 331
  • 10
    Fails to explain the implicit conversion of an array expression to a pointer to its first element, and the cases where that conversion does not happen. – Keith Thompson Jun 28 '14 at 14:35
  • I have to disagree int (*p)[3] declares an array of 3 pointers to int. if the original code had simply declared int *p; the code still could have worked. p[1] and p[2] are still empty after the assignment of the address of array a. p[0] == &a, p[1] and p[2] will be either 0 or undetermined depending on the compiler. – Jeff D. Jun 28 '14 at 14:37
  • @JeffD. you cannot assign a type `int (*)[]` to an integer pointer. – darxtrix Jun 28 '14 at 14:43
  • @KeithThompson by asking *how a pointer to an array is different from the pointer to the first element of the array and name of the array* with an example with `a` and `&a` OP shows he knows there is an implicit conversion from array to the first element of the array. – ouah Jun 28 '14 at 14:43
  • @KeithThompson. I know the cases when the implicit conversion of array type to pointer to the first element of array takes place. – darxtrix Jun 28 '14 at 14:45
  • @JeffD.: To more easily see that you are wrong, compare/contrast with declaration of pointer to function. "int (*p)[3]" declares the symbol "p" to be the address of an array of (compile time fixed to) three integers. – Eric Towers Jun 28 '14 at 17:57
  • @JeffD `int (*p)[3];` → declare p as pointer to array of int; `int *p[3];` or `int *(p[3]);` → declare p as array of pointer to int. See http://cdecl.org if you wish to verify that, but the way I used to remember it when I had trouble: parentheses come first. –  Jun 28 '14 at 18:25
  • I used `printf("%x\n%p\n", a, a);` and got `f0d13d50` and `7ffff0d13d50`, but perhaps it is not worth me pointing out that using `"%d"` with pointers is incorrect for this reason... The only one guaranteed is `"%p"`, and C99 and later allows, but does not require, `"%" PRIdPTR` as well. –  Jun 28 '14 at 23:08
  • 1
    Good grief boys, at no place did I say assign p=&a; Code: int (*p)[3]; int a[3] = { 5, 4, 6 }; int *x; // simple pointer to any integer including arrays of integers x = a; // added p = &a; TRACE("CODE TEST\n"); TRACE("a:%d\t&a:%d\n",a,&a); TRACE("%d\n",*(*p + 2)); TRACE("a:%d\t&a:%d\n",a,&a); TRACE("%d\n",*(x + 2)); TRACE("END CODE TEST\n"); // real world output follows: CODE TEST a:1318424 &a:1318424 6 a:1318424 &a:1318424 6 END CODE TEST – Jeff D. Jun 29 '14 at 05:09
  • 1
    Sorry about the formatting but there is a timer on the edits - note that the code compiles and the real world compiled results match. (I gotta stop doing this late at night, the third TRACE should have dereferenced x - the difference is that &x != &p if you write it this way the value of x is the address of array p the address of x is it's own value a:1579192 &a:1579192 6 x:1579192 &x:1579180 6 – Jeff D. Jun 29 '14 at 05:27
2
  1. a corresponds to the pointer pointing at 0th element of the array. Whereas,the same is the case with &a.It just gives the starting address of the array.

As,a --> pointer pointing to starting element of array a[],it does not know about other element's location..

&a --->address location for storing array a[] which stores first element location,but knows every element's location.

Similarly,other elements location will be (a+2),(a+4) and so upto the end of the array.

Hence,you got such result.

  1. int (*p)[3] is a pointer to the array. had it been int *p[3],it would been meant entirely different. It'd have meant an array of pointers which would have been totally different from this context.

Pointer to an array will automatically take care of all the other elements in the array.In this case,your's is (p);

Whereas,the pointer to the first element of the array,i.e., a will only know about first element of the array.You'll have to manually give pointer arithmetic directions to access next elements.See,in this case---we can get second element from a by adding 2 to a,i.e. a+2,third element by adding 4 to a,i.e., a+4 and so on. // mind the difference of two as it is an integer array!

Am_I_Helpful
  • 18,735
  • 7
  • 49
  • 73
1

In answer to question 1, this is simply an aspect of the C language as designed, unlike most other modern languages C/C++ allows direct manipulation of addresses in memory and has built in facilities to 'understand' that. There are many articles online that explain this better than I could in this small space. Here is one and I am sure there are many others: http://www.cprogramming.com/tutorial/c/lesson8.html

Jeff D.
  • 328
  • 1
  • 7
1

From C99 Standard n1124 6.3.2.1 p3

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

ooga
  • 15,423
  • 2
  • 20
  • 21
1

a and &a have the same value because a long time ago you were required to use the address operator & on arrays to get the array's address, but it is no longer necessary. The name of the array (a in this case) these days just represents the memory address of the array itself, which is also what you get from &a. It's a shorthand that the compiler handles for you.

steviesama
  • 337
  • 4
  • 13
  • 1
    no `a` and `&a` are different things,i.e what the above post tells. – darxtrix Jun 29 '14 at 04:11
  • black_perl, they are different on a lower-level, but he is wondering why they have the same value and it's because they both return the address of the front of the array, which is the point. No need to convolute the explanation unless you are going to add to that instead. – steviesama Jun 29 '14 at 21:16