The usage of %p in C

R

Richard Tobin

even if they were, a void * isn't supposed to be able to store
a value held by another pointer of any kind ?
[/QUOTE]
The point is that it is possible, even if unlikely in most
architectures, for void * to have a different size and/or representation
to, for example, int *.

Even this would not usually be a problem: provided there was a
function prototype in scope the pointer would be converted to void *.
You can perfectly well give an int * argument to free(), for example.
Unfortunately, because printf() is a variadic function, the compiler
can't tell what type is going to be expected, so it can't do the
conversion.

Some compilers do have special knowledge of printf(). Gcc in this
case gives a warning:

warning: format '%p' expects type 'void *', but argument 2 has type 'int *'

-- Richard
 
R

Richard

Who was talking about malloc()ed blocks of memory? We were talking
about

I was. A question if you will.

I'm a bit hazy at the moment but I remember someone harping on about
pointers of one type being of a special type and residing in "special
memory". I suspect it comes out in the wash in that the compiler could
"convert" the "normal storage" to "special" storage unbeknown to the
program.

Then again, I could be meandering too much. I just had a peek
at Facebook. Shockingly crass.

void *s and int *s. They can certainly be laid out differently in
memory, as long as converting from one to the other and back works.
Note: converting. Not reinterpreting the bit patterns. IOW,

int i, *int_ptr=&i;
void *void_ptr=int_ptr;
int *int_ptr2=void_ptr;

must work, and int_ptr2 must compare equal to int_ptr. This is because
the types of int_ptr, int_ptr2 and void_ptr are all known to the
compiler at the point of conversion.

Fair enough.
This is not the case with pointers passed to variadic functions. A
variadic function does _not_ know that it has been passed an int *
instead of the void * it has been told to expect, and what's worse, it
has no way to find out. Therefore, when a variadic function - in this
case, printf() - believes that it needs a void *, it will try to read a
void *, and not an int *. No conversion takes place.

Yes, I understand that. variadic functions weren't really the issue to
my question or pondering.
 
J

Jack Klein

I think the cast is unnecessary, unless int * and void *
are of different sizes in your system...

Actually there could be conforming implementations where this code
could fail even if both pointer types have the same size. I think I
might actually have used one, a quarter century or so ago, but I can't
remember for sure.

Same size does not necessarily guarantee passing and/or returning in
the same manner (same register, same stack location, same memory
block, whatever).

Not quite the same thing, I do remember compilers for the Motorola
68000 series (old Apple, Atari, Amiga computers) that would return
integer types in a specific D register, pointers in one of the A
registers.

So failure to include a prototype for malloc() and using the cast:

char *cp = (char *)malloc(some_size);

....would result in the pointer being initialized with some totally
untreated value from a data register, completely ignoring the actual
pointer in an address register.

The point is, passing anything other than pointer to void to printf()
to match a "%p" conversion specifier is undefined behavior. Even if
it works on most compilers. Even if it works on all the compilers you
have tried it on.

There are reasons to write a program that uses implementation-defined,
and even technically undefined behavior, if you know what your
implementation does with them. Feeling like you can't be bothered to
write the "(void *)" cast in one of the few situations in C where such
a cast is not necessary is not one of the valid reasons.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
J

Jack Klein

The correction is correct ;-)

Anyway, a question raises:

1) two pointer types are allowed to be of different sizes

Yes, that is correct.
2) the void pointer is guaranteed to store any other pointer's value
without loss of information

Well, that is almost correct. There is a guarantee with pointer to
void with respect to pointer to any object type. There is no
correspondence, defined conversion, or any other guarantee between
pointers to objects and pointers to functions.

Here is what the standard says:

"A pointer to void may be converted to or from a pointer to any
incomplete or object type. A pointer to any incomplete or object type
may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer."
this leads to:
3) the size of a void pointer is guaranteed to be at least as big as the
size of any other pointers

If you read the quotation from the standard above, you will note that
it requires that pointer to void be able to hold all the useful
information from any other pointer to object type. A perverse
implementation could have pointers to void and character types
actually occupy less space than pointers to other object types,
although those larger pointers to other types could not use more bits.
if 3) is a correct deduction, then I don't see the point to impose that
an argument of printf used in conjunction with %p must be a void pointer.

There is nothing that requires that a compiler pass pointer to void
and, for example, pointer to int, in the same manner to functions.
printf() is a variadic function, where there is no prototype to tell
the compiler to convert the parameters, if necessary.

What happens if a particular compiler passes pointer to void in
register R3 and pointer to int in register R5?

Mainly, it is undefined behavior because the C standard says that it
is.
if 3) is incorrect, how do we assure that a conversion to void * cannot
lead to a loss of information?

Again, 3) is not necessarily correct. A compiler for a platform with
a 32-bit address space could have 32 bit pointers to void, and 64 bit
pointers to int, although it could only use 32 bits of the pointer to
int.
Thanks for clarifying this point.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
C

Chris Torek

1) two pointer types are allowed to be of different sizes
2) the void pointer is guaranteed to store any other pointer's value
without loss of information

Any "data pointer", anyway. (That is, "void *" might not be able
to hold a pointer-to-function type. This is the case in some MS-DOS
compiler models, for instance, when functions use 32-bit addresses
and "data bytes" live in a 16-bit address space.)
this leads to:
3) the size of a void pointer is guaranteed to be at least as big as the
size of any other pointers

Not necessarily -- it just means that "void *" values have enough
data to *reconstruct* any *valid* data pointer.

Imagine, for instance, that for some reason "int *" uses a megabyte,
but only 39 bits of that megabyte really specify the actual memory
address. Then "void *" might fit in a 21-byte object, with 39 bits
of those 29 bytes holding the "important" 39 bits of the "int *".
Note that a different subset of the "void *" bits might hold the
"important" bits of "short *", and so on.

(On Real Machines, of course, there are only three or four actual
pointer formats, typically 4, 8, and 16 or so bytes each.)
if 3) is a correct deduction, then I don't see the point to impose that
an argument of printf used in conjunction with %p must be a void pointer.

Perhaps "int *" values are passed in red registers, and "void *"
values in blue registers. (Most integers go in green registers.
Yellow registers are for float, purple for double, and the black
registers are reserved to the secret part of the system. :) )
if 3) is incorrect, how do we assure that a conversion to void * cannot
lead to a loss of information?

This is up to the implementation -- the C Standard requires that
the implementation achieve it *somehow*, though.
 
S

Stephen Sprunk

jacob navia said:
I think the cast is unnecessary, unless int * and void *
are of different sizes in your system...

Without the cast it's UB, period. If it happens to do what the programmer
expects on systems where sizeof(int*) == sizeof(void*), that's one possible
result of UB.

S
 
K

Kenneth Brody

Richard said:
I keep hearing this. Can you explain how,say, a "special" pointer can be
stored in a malloc'ed block of memory which basically returns a single
block from a common memory pool?

No need to get malloc() and friends involved.

Consider:

char *;
and
char (*)(char);

One points to a char, the other to a function that takes and returns
a char. One points to data, the other to code. Who says that the
way you point to data has any relation to how you point to code?

First, you have the simple case of segmented memory architecture,
where you may have 32-bit code pointers and 48-bit data pointers.

Next, there are cases where the pointers may be the same size, but
formatted differently. I once worked on a platform with 36-bit
words and 18-bit addresses. Within a 36-bit word, you could hold
a "pointer" to bits within an address. (ie: 18 bits for address,
plus additional bits to point within the 36-bit value stored there.)
While I never used a C compiler on that system, it is quite
possible that a pointer to code would be a straight 18-bit value,
with the other bits ignored, while a pointer to data would be the
18-bit address plus the additional bits to point within the data.
And yet, they would both be 36 bit values.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
M

Mark McIntyre

I think the cast is unnecessary, unless int * and void *
are of different sizes in your system...

In fact the cast is strictly speaking *required*, precisely _because_
you cannot guarantee that pointer sizes are the same.

Obviously if you don't need to worry about portability and never
expect to get a new compiler or OS, then....
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Mark McIntyre

Actually there could be conforming implementations where this code
could fail even if both pointer types have the same size. I think I
might actually have used one, a quarter century or so ago, but I can't
remember for sure.

You're probably thinking of MacOS or possibly TOS, where different
types sometimes got passed in different registers.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
K

Keith Thompson

Richard said:
I was. A question if you will.

I'm a bit hazy at the moment but I remember someone harping on about
pointers of one type being of a special type and residing in "special
memory". I suspect it comes out in the wash in that the compiler could
"convert" the "normal storage" to "special" storage unbeknown to the
program.
[...]

Different pointer types can have different representations, but
malloc()ed memory can hold objects of any type. A conforming
implementation can't restrict certain types of objects to be stored
only in certain memory regions. (If such "special" objects are to be
provided, the implementation can provide access to them, but not as
ordinary C objects; there might be a compiler extension.)
 
R

Richard Tobin

I think the cast is unnecessary, unless int * and void *
are of different sizes in your system...
[/QUOTE]
In fact the cast is strictly speaking *required*, precisely _because_
you cannot guarantee that pointer sizes are the same.

I think that even if the pointer sizes are the same, and they have
the same representation, nothing stops an implementation from passing
int * and void * pointers to variadic functions in different ways.

In fact, I don't see anything that stops it from passing void * and char *
in different ways, which seems like a defect to me.

-- Richard
 
?

=?iso-2022-kr?q?=1B=24=29CHarald_van_D=0E=29=26=0F

I think that even if the pointer sizes are the same, and they have the
same representation, nothing stops an implementation from passing int *
and void * pointers to variadic functions in different ways.

In fact, I don't see anything that stops it from passing void * and char
* in different ways, which seems like a defect to me.

void * and char * may be passed in different ways if and only if va_arg
can still be used to access either as the other. If you use it to access
a void * argument as were it a char *, or vice versa, you get the same
value. There's a special exception in the definition of va_arg that makes
this so.

But yes, for printf you have to get the type right, since it need not be
implemented using va_arg.
 
A

Army1987

Tak said:
I want to know how to use %p in the program.Help me! [snip]
Does it mean %x?

No. It means %p. It prints a pointer. _How_ that pointer is printed is
system-dependent. Like %x is a possibility. So is (segment like
%4.4X):(offset like %4.4X). So are many other options. It will, in all
likelyhood, depend on how pointers are usually printed on your platform.

What am I missing which causes the last sentence not to be a
tautology, so that "in all likelyhood" is needed instead of
"obviously"?
 
A

Army1987

There are reasons to write a program that uses implementation-defined,
and even technically undefined behavior, if you know what your
implementation does with them. Feeling like you can't be bothered to
write the "(void *)" cast in one of the few situations in C where such
a cast is not necessary is not one of the valid reasons.
You meant "...a cast is necessary..."?
 
K

Keith Thompson

Army1987 said:
Tak said:
I want to know how to use %p in the program.Help me! [snip]
Does it mean %x?

No. It means %p. It prints a pointer. _How_ that pointer is printed is
system-dependent. Like %x is a possibility. So is (segment like
%4.4X):(offset like %4.4X). So are many other options. It will, in all
likelyhood, depend on how pointers are usually printed on your platform.

What am I missing which causes the last sentence not to be a
tautology, so that "in all likelyhood" is needed instead of
"obviously"?

It depends on the meaning of "platform". It's possible, for example,
that an operating system might have a convention of printing pointers
in octal, but a C implementation running on that OS might print them
in hexadecimal.

If "platform" is synonymous with "implementation", then yes, it's a
tautology.
 
O

Old Wolf

int i = 10;

printf("hex -> %x\n", i);

It could be argued (and has been, in fact) that this
causes undefined behaviour. The standard specifies that
%x requires an unsigned int argument.

I can't see anything other than a DS9000 failing it, though.
 
K

Keith Thompson

Old Wolf said:
It could be argued (and has been, in fact) that this
causes undefined behaviour. The standard specifies that
%x requires an unsigned int argument.

I can't see anything other than a DS9000 failing it, though.

On the other hand, even on a DS9000 the following:

printf("hex -> %x\n", (unsigned)i);

will work properly (unless there's an I/O error).

If you're writing new code, it's easier to use exactly the right type
in the first place than to prove that it's ok to use a slightly
different type.

But if you see the above in somebody else's program that isn't
working, it should be safe to look elsewhere for the source of the
bug.
 
R

Richard Bos

[ Snip! ]
Well, that is almost correct. There is a guarantee with pointer to
void with respect to pointer to any object type. There is no
correspondence, defined conversion, or any other guarantee between
pointers to objects and pointers to functions.

Here is what the standard says:

"A pointer to void may be converted to or from a pointer to any
incomplete or object type. A pointer to any incomplete or object type
may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer."


If you read the quotation from the standard above, you will note that
it requires that pointer to void be able to hold all the useful
information from any other pointer to object type. A perverse
implementation could have pointers to void and character types
actually occupy less space than pointers to other object types,
although those larger pointers to other types could not use more bits.

Yes, they could; but those extra bits could not be used to hold required
information, and a comparison of pointers that are equal, except that
one has information in the extra bits and the other lost that extra
information in passing through a void * and back, must compare equal.
Two pointers which both have information in the extra bits are still
allowed to compare different, AFAICT. (One reason for doing this is to
provide debugging information. It may not be a _good_ reason to do so
for normal pointers, but not for void * and char *; but it is a legal
one.)

Richard
 
M

Michal Nazarewicz

[ Snip! ]
Well, that is almost correct. There is a guarantee with pointer to
void with respect to pointer to any object type. There is no
correspondence, defined conversion, or any other guarantee between
pointers to objects and pointers to functions.

Here is what the standard says:

"A pointer to void may be converted to or from a pointer to any
incomplete or object type. A pointer to any incomplete or object type
may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer."


If you read the quotation from the standard above, you will note that
it requires that pointer to void be able to hold all the useful
information from any other pointer to object type. A perverse
implementation could have pointers to void and character types
actually occupy less space than pointers to other object types,
although those larger pointers to other types could not use more bits.

Yes, they could; but those extra bits could not be used to hold required
information, and a comparison of pointers that are equal, except that
one has information in the extra bits and the other lost that extra
information in passing through a void * and back, must compare equal.
Two pointers which both have information in the extra bits are still
allowed to compare different, AFAICT.

No, they are not. The same way as integers with different padding bits
but the same value bits are required to compare equal.
(One reason for doing this is to provide debugging information. It may
not be a _good_ reason to do so for normal pointers, but not for void
* and char *; but it is a legal one.)

If some debug information were stored in those padding bits
implementation would be forced to ignore them when comparing pointers.
(Well, it is possible to create a pointer where those extra bits store
some kind of CRC in which case implementation could use it while
comparing pointers.)
 
A

Army1987

On the other hand, even on a DS9000 the following:

printf("hex -> %x\n", (unsigned)i);

will work properly (unless there's an I/O error).
<nitpick>
If there's an I/O error it will return a negative value, which is
the proper behavior in that case.
</nitpick>
:)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top