Velocity Reviews > Address of array && address of pointer to array

Stanley Rice
Guest
Posts: n/a

 09-14-2011
Hello all

I am confused of array and pointer these days, and wrote some testing
program. one of the snippet is as below:

int A[3];

printf("%p\n", A); // 1
printf("%p\n", &A); // 2
printf("%p\n", A + 1); // 3
printf("%p\n", &A + 1); // 4

It shows that the result of first and second statement is the same,
while the result of statement 3 and statement 4 is different. I can
understand the difference of result 3 and result 4, as if the pointer
to arrays are of different levels. But why the statement 1 and
statement 2 generate the same result?

What's more, when we initialize the pointer to a function. we can both
assign to the pointer with just a function name, as well as the
address of the function name, using &(ampersand) operator. Why we
could do so.

Ben Bacarisse
Guest
Posts: n/a

 09-14-2011
Stanley Rice <(E-Mail Removed)> writes:

> I am confused of array and pointer these days, and wrote some testing
> program. one of the snippet is as below:
>
> int A[3];
>
> printf("%p\n", A); // 1
> printf("%p\n", &A); // 2
> printf("%p\n", A + 1); // 3
> printf("%p\n", &A + 1); // 4
>
> It shows that the result of first and second statement is the same,
> while the result of statement 3 and statement 4 is different. I can
> understand the difference of result 3 and result 4, as if the pointer
> to arrays are of different levels. But why the statement 1 and
> statement 2 generate the same result?

Nit: convert the pointers to (void *) because that's what %p expects.
If you do that, 1 and 2 will always produce the same result no matter
what type the array has. I've used a machine where they won't always be
the same if the type of A is char[3].

The differences and potential differences come from the fact that A and
&A are different things. When an array is used in most contexts, it is
converted to a pointer to its first element. Being the operand of the
& operator is an exception to this rule. So in case 1, A is an 'int *'
and in case 2, &A is a pointer to the whole array (the type is
'int (*)[3]' if you are interested in the details).

On most systems, a pointer to the start of an array and a pointer to the
whole array will print the same using %p and they are guaranteed to be
the same when converted to a common type. I.e.

(void *)A == (void *)&A[0] && (void *)A == (void *)&A

is always true. The types are different and that means that they may
have different representations so %p might print different things for
in your unconverted printf calls (1 and 2).

The difference in 3 and 4 is due to the "levels" as you put it. Adding
to a pointer adds the size of the thing pointed to. A + 1 adds one to
an int * producing a pointer to A[1]. In fact, A[n] is defined to be
*(A + n). &A + 1 produces a pointer that points just past the end of
the array A.

I thought this might be FAQ (http://c-faq.com/) but I could not find the
-- it's good stuff.

> What's more, when we initialize the pointer to a function. we can both
> assign to the pointer with just a function name, as well as the
> address of the function name, using &(ampersand) operator. Why we
> could do so.

"Why" is never easy in C. That's just the way it is: the name of a
function is converted to a pointer to that function except when the
function name is the operand of &. Thus fname and &fname are always the
same thing.

--
Ben.

Paul N
Guest
Posts: n/a

 09-14-2011
On Sep 14, 2:41*pm, Ben Bacarisse <(E-Mail Removed)> wrote:
> Stanley Rice <(E-Mail Removed)> writes:
> > I am confused of array and pointer these days, and wrote some testing
> > program. one of the snippet is as below:

>
> > int A[3];

>
> > printf("%p\n", A); * * * * *// * 1
> > printf("%p\n", &A); * * * * // * 2
> > printf("%p\n", A + 1); * * *// * 3
> > printf("%p\n", &A + 1); * * // * 4

>
> > It shows that the result of first and second statement is the same,
> > while the result of statement 3 and statement 4 is different. I can
> > understand the difference of result 3 and result 4, as if the pointer
> > to arrays are of different levels. But why the statement 1 and
> > statement 2 generate the same result?

(snip)

> I thought this might be FAQ (http://c-faq.com/) but I could not find the
> -- it's good stuff.

Yes, 6.12 is the one. Though I managed to miss it too, and only found
it by remembering the wording and using Google!

Peter Nilsson
Guest
Posts: n/a

 09-15-2011
Ben Bacarisse <(E-Mail Removed)> wrote:
> Stanley Rice <(E-Mail Removed)> writes:
> > int A[3];
> >
> > printf("%p\n", A); // 1
> > printf("%p\n", &A); // 2
> > printf("%p\n", A + 1); // 3
> > printf("%p\n", &A + 1); // 4

<snip>
>
> Nit: convert the pointers to (void *) because that's what %p
> expects.

True.

> If you do that, 1 and 2 will always produce the same result no
> matter what type the array has.

That's not true. If the OP wants to know if they compare equal,
then the equality operater should be used. There is no guarantee
that %p prints exactly the same thing, even for pointer arguments
with the same representation. [e.g segmented architectures.]

The only guarantee is that if you read the output with scanf (et
al,) the conversion will compare equal with the original pointer

<snip>

--
Peter

Ben Bacarisse
Guest
Posts: n/a

 09-15-2011
Peter Nilsson <(E-Mail Removed)> writes:

> Ben Bacarisse <(E-Mail Removed)> wrote:
>> Stanley Rice <(E-Mail Removed)> writes:
>> > int A[3];
>> >
>> > printf("%p\n", A); // 1
>> > printf("%p\n", &A); // 2
>> > printf("%p\n", A + 1); // 3
>> > printf("%p\n", &A + 1); // 4

> <snip>
>>
>> Nit: convert the pointers to (void *) because that's what %p
>> expects.

>
> True.
>
>> If you do that, 1 and 2 will always produce the same result no
>> matter what type the array has.

>
> That's not true. If the OP wants to know if they compare equal,
> then the equality operater should be used. There is no guarantee
> that %p prints exactly the same thing, even for pointer arguments
> with the same representation. [e.g segmented architectures.]

Good point. Once converted the pointers will compare equal but even
equal pointers might print differently.

<snip>
--
Ben.

Stanley Rice
Guest
Posts: n/a

 09-15-2011
On Sep 14, 9:41*pm, Ben Bacarisse <(E-Mail Removed)> wrote:
> Stanley Rice <(E-Mail Removed)> writes:
> > I am confused of array and pointer these days, and wrote some testing
> > program. one of the snippet is as below:

>
> > int A[3];

>
> > printf("%p\n", A); * * * * *// * 1
> > printf("%p\n", &A); * * * * // * 2
> > printf("%p\n", A + 1); * * *// * 3
> > printf("%p\n", &A + 1); * * // * 4

>
> > It shows that the result of first and second statement is the same,
> > while the result of statement 3 and statement 4 is different. I can
> > understand the difference of result 3 and result 4, as if the pointer
> > to arrays are of different levels. But why the statement 1 and
> > statement 2 generate the same result?

>
> Nit: convert the pointers to (void *) because that's what %p expects.
> If you do that, 1 and 2 will always produce the same result no matter
> what type the array has. *I've used a machine where they won't always be
> the same if the type of A is char[3].
>
> The differences and potential differences come from the fact that A and
> &A are different things. *When an array is used in most contexts, it is
> converted to a pointer to its first element. *Being the operand of the
> & operator is an exception to this rule. *So in case 1, A is an 'int *'
> and in case 2, &A is a pointer to the whole array (the type is
> 'int (*)[3]' if you are interested in the details).
>
> On most systems, a pointer to the start of an array and a pointer to the
> whole array will print the same using %p and they are guaranteed to be
> the same when converted to a common type. *I.e.
>
> * (void *)A == (void *)&A[0] && (void *)A == (void *)&A
>
> is always true. *The types are different and that means that they may
> have different representations so %p might print different things for
> in your unconverted printf calls (1 and 2).
>
> The difference in 3 and 4 is due to the "levels" as you put it. *Adding
> to a pointer adds the size of the thing pointed to. *A + 1 adds one to
> an int * producing a pointer to A[1]. *In fact, A[n] is defined to be
> *(A + n). *&A + 1 produces a pointer that points just past the end of
> the array A.
>
> I thought this might be FAQ (http://c-faq.com/) but I could not find the
> -- it's good stuff.
>
> > What's more, when we initialize the pointer to a function. we can both
> > assign to the pointer with just a function name, as well as the
> > address of the function name, using &(ampersand) operator. Why we
> > could do so.

>

(snip)
> "Why" is never easy in C. *That's just the way it is: the name of a
> function is converted to a pointer to that function except when the
> function name is the operand of &. *Thus fname and &fname are always the
> same thing.

So What you mean is that the compiler does most of things for us? I
have tried to compare the address of function, say

void foo() { }

assert((void *)foo == (void *)&foo);

and it passes. Is it the reason as the issue of the array talk above ?

Stanley Rice
Guest
Posts: n/a

 09-15-2011
On Sep 15, 5:54*pm, Ben Bacarisse <(E-Mail Removed)> wrote:
> Peter Nilsson <(E-Mail Removed)> writes:
> > Ben Bacarisse <(E-Mail Removed)> wrote:
> >> Stanley Rice <(E-Mail Removed)> writes:
> >> > int A[3];

>
> >> > printf("%p\n", A); * * * * *// * 1
> >> > printf("%p\n", &A); * * * * // * 2
> >> > printf("%p\n", A + 1); * * *// * 3
> >> > printf("%p\n", &A + 1); * * // * 4

> > <snip>

>
> >> Nit: convert the pointers to (void *) because that's what %p
> >> expects.

>
> > True.

>
> >> If you do that, 1 and 2 will always produce the same result no
> >> matter what type the array has.

>
> > That's not true. If the OP wants to know if they compare equal,
> > then the equality operater should be used. There is no guarantee
> > that %p prints exactly the same thing, even for pointer arguments
> > with the same representation. [e.g segmented architectures.]

<snip>
> Good point. *Once converted the pointers will compare equal but even
> equal pointers might print differently.

Thanks for you replying. But I am sorry that I am not fully understand
that.

1. when we use %p to print the pointer, but the argument is not
explicitly converted, will it implicitly be converted to (void *)
type?
2. Why the equal pointers will print differently? It's machine
dependent or anything else? Can we say that, two pointers must be
equal if they are printed the same.
3. In my previous post. If I change my code:
printf("the address of A+1: %p\n", (void *)A+1); // 3'
printf("the address of &A+1: %p\n", (void *)&A+1); // 4'

statement 3' and 4' will print the same thing. Why it that compared to
my original code, without explicitly cast.

Ike Naar
Guest
Posts: n/a

 09-15-2011
On 2011-09-15, Stanley Rice <(E-Mail Removed)> wrote:
> 1. when we use %p to print the pointer, but the argument is not
> explicitly converted, will it implicitly be converted to (void *)
> type?

No.

> 2. Why the equal pointers will print differently? It's machine
> dependent or anything else? Can we say that, two pointers must be
> equal if they are printed the same.

Imagine a cpu architecture where pointers are represented by a pair
(base,offset) where the effective memory address is obtained by

address = 16 * base + offset

The pointer (2,25) represents effective memory address 16 * 2 * 25 = 57 .
The pointer (3,9) represents effective memory address 16 * 3 + 9 = 57 .
These two pointers could print differently (say, "2:25" vs. "3:9") but
compare equal (57 vs. 57).

> 3. In my previous post. If I change my code:
> printf("the address of A+1: %p\n", (void *)A+1); // 3'
> printf("the address of &A+1: %p\n", (void *)&A+1); // 4'

Beware: (void *)A+1 is parsed as ((void *)A) + 1 which is
not correct C (one cannot perform arithmetic on a pointer to void)
although gcc will probably accept it as an extension.

You probably meant (void *) (A+1)

Nobody
Guest
Posts: n/a

 09-15-2011
On Thu, 15 Sep 2011 05:36:48 -0700, Stanley Rice wrote:

> 1. when we use %p to print the pointer, but the argument is not
> explicitly converted, will it implicitly be converted to (void *)
> type?

No. The compiler doesn't know the types of printf's arguments (other than
the first one), so it can't know that it should be a void*.

> 2. Why the equal pointers will print differently? It's machine
> dependent or anything else?

The format used by "%p" is implementation-dependent.

One reason why equal pointers might print differently is if there are
multiple representations which are considered equal, and the output for
"%p" is based upon the representation without any form of normalisation.

E.g. on 8086, a (far) pointer contains a 16-bit segment and a 16-bit
offset. The actual memory address is "segment * 16 + offset", so e.g.
0100:2300 and 0200:1300 both refer to the address 0x03300. If
comparison normalises the pointers, they will compare equal. If printing
uses the raw representation, they will print differently.

> Can we say that, two pointers must be equal if they are printed the same.

No. The format used by "%p" is implementation-dependent. An
implementation is free to print e.g. "?" for every pointer.

> 3. In my previous post. If I change my code:
> printf("the address of A+1: %p\n", (void *)A+1); // 3'
> printf("the address of &A+1: %p\n", (void *)&A+1); // 4'
>
> statement 3' and 4' will print the same thing. Why it that compared to
> my original code, without explicitly cast.

You're relying upon a implementation-specific extension. Type casts bind
more tightly than addition, so "(void *)A+1" is parsed as "((void *)A)+1".
This is invalid according to the standard ("void" doesn't have a size, so
you can't perform arithmetic on a "void*").

Implementations which allow arithmetic on void* (e.g. gcc) act as if
sizeof(void)==1 in such cases. Because you convert before adding, both
expressions have the same value.

The reason why the original expressions had different values is that, in
pointer arithmetic, the offset is the number of items rather than the
number of bytes, so the calculation scales the offset by the size of the
type being pointed to.

So "A+1" is one int past A ("A" is a pointer to int) while "&A+1" is one
array of 3 ints past A ("&A" is a pointer to an array of 3 ints).

To correctly apply an explicit cast to your original code, you should have
written:

printf("the address of A+1: %p\n", (void *)(A+1)); // 3'
printf("the address of &A+1: %p\n", (void *)(&A+1)); // 4'

This will (typically) produce the same result as before, except that this
version doesn't rely upon implementation-specific behavior (i.e. all
pointers using the same representation).

James Kuyper
Guest
Posts: n/a

 09-15-2011
On 09/15/2011 08:36 AM, Stanley Rice wrote:
....
> 1. when we use %p to print the pointer, but the argument is not
> explicitly converted, will it implicitly be converted to (void *)
> type?

No, and that's what makes the behavior undefined. That's because
printf() accepts a variable list of arguments. For the variable part of
it's argument list, the only implicit conversions that occur are the
default argument promotions.

> 2. Why the equal pointers will print differently? It's machine
> dependent or anything else? Can we say that, two pointers must be
> equal if they are printed the same.

As a general rule, what's printed by the printf("%p, ... ) is related to
the representation of a pointer. The standard does not require that
pointers pointing to the same location have the same representation,
only that they must compare equal. Real implementations have used
representations for which such a requirement would be a problem. I've
used machines where addresses were represented by a 16-bit segment and a
16-bit offset, and the physical address was 16*segment+offset, so that
there were a great many different ways of forming pointers with
different representations that point at the same location.

Yes, it is machine dependent. In principle, even different
implementations for the same machine could implement pointers
differently, but that's rather unlikely.

I believe that two pointers that print the same must compare equal; but
not vice-versa.

> 3. In my previous post. If I change my code:
> printf("the address of A+1: %p\n", (void *)A+1); // 3'
> printf("the address of &A+1: %p\n", (void *)&A+1); // 4'
>
> statement 3' and 4' will print the same thing. Why it that compared to
> my original code, without explicitly cast.

(void*)A+1 is a constraint violation, because (void*)A is a pointer, and
is not a pointer to an object type. Many compilers operate, by default,
in a non-conforming mode that allows such expressions. They treat it as
the rough equivalent of (void*)((char*)A + 1). Since A and &A are
pointers of different types that point at the same location.
(void*)((char*)&A + 1) points at the same location a (void*)((char*)A +
1), which is why the results you got were the same.

If your compiler allows such code, find out if you can disable that
"feature". For instance, with gcc, use "-Wpointer-arith". It represents
a fundamental misconception of what void* means to allow pointer
arithmetic on void*. Before the inventions of void*, char* was used for
the same purpose. void* was invented specifically to indicate that your
code is NOT keeping track of what type of thing the pointer points at.
It differs from char* in two important ways. The first way is that
pointers to other object types convert to and from void* implicitly,
which greatly simplifies typical uses of void*.

The second way, and the one that is relevant here, is that it is a
constraint violation to use a pointer to void in any context that
requires knowledge of the type of object that the pointer points at. In
particular, if p is a pointer to an object type, and n is an integer,
the value of p+n points at a location that is farther along in memory by
exactly enough space for n objects of the type that p points at. In
other words, (char*)(p+n) = (char*)p + n*sizeof *p. However, if p is a
pointer to void, there is NO type type p points at. sizeof *p and
sizeof(void) both constraint violations, as is p+n. If you don't want to
get diagnostic messages as a result of writing code like p+1, you should
make sure that p is a pointer to an object type, such as char*, not a
pointer to void.