James Kuyper
9/15/2011 2:21:00 PM
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.