5

I've got confused when learning array and pointer in C language,why are ch, &ch,&ch[0] just equal to one another ,while sptr, &sptr,&sptr[0] are not? Here's my source code:

int main(void)
    {
        char ch[7] = { '1','2','3','4','5','6','0' };
        char *sptr = "132456";
        double db[4] = { 1.0,2.0,3.0,4.0 };
        printf("\n%p  %p  %p\n", ch, &ch,&ch[0]);
        printf("\n%p  %p  %p\n", sptr, &sptr,&sptr[0]);
        printf("\n%p  %p  %p\n", db, &db,&db[0]);
        return 0;

    }

And the inputs on my machine are:

00FDFD68  00FDFD68  00FDFD68

00037CD0  00FDFD5C  00037CD0

00FDFD34  00FDFD34  00FDFD34
CircleBop
  • 67
  • 3
  • 1
    [Also, check this](http://stackoverflow.com/a/41329292/2173917) – Sourav Ghosh Apr 21 '17 at 12:23
  • The `printf`s all invoke undefined behaviour for passing the wrong type for the `%p` conversion type specifier. – too honest for this site Apr 21 '17 at 12:55
  • @Olaf is right; to be correct, you should cast each of the values to `(void *)`. On most real systems it won't make any difference, but it is better to be correct where possible, and certain other similar "harmless" mistakes have been known to cause problems. – davmac Apr 21 '17 at 13:06

2 Answers2

10

In most (though not all) contexts an array "decays" into a pointer to its first element. This is why ch and &ch[0] are the same in your example (array element access has higher precedence than the "address of" operator, so the latter could also be written as &(ch[0])).

The remaining &ch is one case where the array does not decay into a pointer; instead, you get the address of the array. Naturally enough, this is the same as the address of the first element of the array - however, importantly, it has a different type; it is of type char (*)[7], i.e. a pointer to an array of char with 7 elements. The other two pointers are of type char *, i.e. a pointer to an individual char.

Since sptr is a pointer, &sptr is the address of that pointer and naturally will be different. &sptr[0] is equivalent to sptr + 0, which is of course equal to sptr.

That you do not see why sptr and &sptr yield different addresses indicates a misunderstanding of what a pointer is. A pointer is a fixed-size object with a value that can refer to (point at) some arbitrary object of a particular type. Because it is an object itself, a pointer can be made to point at a different object. An array variable, on the other hand, always (during its lifetime) refers to the same array object.

In your example output:

00037CD0  00FDFD5C  00037CD0

The first value, 00037CD0, is the location to which sptr points - that is, it is the location in memory of the string constant "132456". The second value, 00FDFD5C, is the address of the sptr variable itself. What this shows is that there is a pointer object at address 00FDFD5C which holds the value 00037CD0.

Essentially, the difference between the two cases boils down to this:

  • The address of an array is the same as the address of its first element
  • The address of a pointer, on the other hand, bears no relation to what the pointer currently points to.
davmac
  • 20,150
  • 1
  • 40
  • 68
  • "This is why ch and &ch[0] **appear** the same in your example" - They don't appear to be the same. They are not and the compiler should generate a warning when assigning. (note that users are expected to enable and fix warnings before asking) – too honest for this site Apr 21 '17 at 12:41
  • @Olaf we've had this discussion before. I don't think it bears repeating, but I will summarise: the address in storage of a first element of an array _necessarily_ has the same address in storage as the array itself. The "address of" operator returns a pointer comprising an address value and a type; the type will be different between the two cases, and this is already clear in the answer. – davmac Apr 21 '17 at 12:44
  • Hmm, I have to appologise this time. Your phrasing is unfortunate. "appear" is too weak here. They don't just appear the same, but yield the **exactly** same result **here** (value and type, there is no difference). That has confused me. To get me right: The fault is on my side this time, but please rephrase to emphasise they are equivalent in all aspects **here**. – too honest for this site Apr 21 '17 at 12:54
  • 1
    @Olaf, ok, I've changed "appear" to "are". Kindly retract your down-vote. – davmac Apr 21 '17 at 12:56
  • Done. But see my comment at the question. – too honest for this site Apr 21 '17 at 12:58
  • Detail: "The address of an array is the same as the address of its first element" --> the 2 addresses will _equate_ to each other, they need not have the same binary representation. Being different types, the size of the two pointers type may even differ. Certainly having the same binary representation and size is the over-whelming common implementation detail. Yet old, odd and maybe new novel architectures may differ. – chux - Reinstate Monica Apr 21 '17 at 14:35
  • @chux agreed that they need not have the same representation, though on OP's system they certainly do. :) By "address of an array is the same as the address of its first element" I'm talking about location in storage, not about representation or even value. (Strictly speaking, the value of the two pointers can't be compared anyway; but in practice most C compilers do allow such a comparison). – davmac Apr 21 '17 at 14:46
  • @davmac True about those 2 pointers can not compare directly, yet could through `(void*)`. – chux - Reinstate Monica Apr 21 '17 at 15:09
5

If we "draw" it out, your array ch will look like this in memory:

+-----+-----+-----+-----+-----+-----+-----+
| '1' | '2' | '3' | '4' | '5' | '6' | '0' |
+-----+-----+-----+-----+-----+-----+-----+
^     ^
|     |
|     &ch[1]
|
&ch
^
|
&ch[0]

As you can see both &ch and &ch[0] point to the same location. But there is an important difference between the two expressions: &ch is a pointer to the array while &ch[0] is a pointer to an element in the array. The two pointers have different types: &ch have the type char (*)[7] while &ch[0] have the type char *. Even if both are pointers to the same location the difference in type makes it semantically very different.

Now for how array fits into it all. For any array or pointer a and index i, the expression a[i] is equal to *(a + i). That means that &a[i] is equal to &*(a + i). The address-of and dereference operators cancels each other out, meaning that &a[i] is equal to (a + i). If the index is zero we have &a[0] being equal to (a + 0), but since adding zero to anything is a no-op it is also equal to (a) which is equal to a. Therefore &a[0] is equal to a. When you see someone saying that an array decays to a pointer to its first element, this is what they mean. Using the array a in an expression is the same as using &a[0].


On an unrelated note, the array ch might be an array of characters, but it's not a string. That's because in C strings are really called null terminated strings. The word "null" in this case is not the null pointer but the integer (not character) zero.

That means you can not use ch for any function that requires a string as that will lead to undefined behavior as those go out of bounds looking for the terminator.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621