printf() and void *

I

Ioannis Vranos

I recall from previous discussions here, that we must cast a non-void
pointer to void * before printing its value with printf("%p"). Is it
true, and if yes why?
 
H

Harald van Dijk

I recall from previous discussions here, that we must cast a non-void
pointer to void * before printing its value with printf("%p"). Is it
true, and if yes why?

Except for function pointers, which can't be printed portably at all, it
is true. There are no implicit conversions from other pointer types to
void * in the variable arguments of a function, so if any pointer type
would be accepted, how could printf know which one was passed?
 
R

Richard Heathfield

Harald van D?k said:
Except for function pointers, which can't be printed portably at all, it
is true.

Well, no pointer value can be printed portably, since the output associated
with %p is implementation-defined. But if you mean there's no *technique*
for printing a function pointer value in ISO C, I must disagree. (If you
simply mean that there's no printf format specifier for it, then of course
you're right.)

#include <stdio.h>

int main(void)
{
int (*mp)(void) = main;
unsigned char *ucp = (unsigned char *)&mp;
size_t len = sizeof mp;
while(len--)
{
printf("%02X", *ucp++);
}
puts("");

return 0;
}

<snip>
 
K

Keith Thompson

Richard Heathfield said:
Harald van D?k said:

Well, no pointer value can be printed portably, since the output associated
with %p is implementation-defined. But if you mean there's no *technique*
for printing a function pointer value in ISO C, I must disagree. (If you
simply mean that there's no printf format specifier for it, then of course
you're right.)

#include <stdio.h>

int main(void)
{
int (*mp)(void) = main;
unsigned char *ucp = (unsigned char *)&mp;
size_t len = sizeof mp;
while(len--)
{
printf("%02X", *ucp++);
}
puts("");

return 0;
}

On a system with CHAR_BIT > 8, the above would wor, but it could
produce ambiguous results. For example, the output "12345" could be
result of bytes 0x123 and 0x45 or 0x12 and 0x345.

I don't believe there are any hosted implementations (i.e., ones
required to support <stdio.h> with CHAR_BIT > 8), so this is unlikely
to be an issue in practice -- though the systems, mostly DSPs, that do
have CHAR_BIT > 8 may well support stdio even though they're not
required to.

Another possible issue with the above is the order in which it prints
the bytes. On one system, I ran the above code with an added line:

printf("main = %p\n", (void*)main); /* non-portable extension */

and got:

A4830408
main = 0x80483a4

There are always tradeoffs. If you want to ensure that the output is
sensible for the platform's address representation, *and* if the
implementation supports converting a function pointer to void*, *and*
if you don't mind your code being non-portable, you can use "%p" with
a cast. On systems that don't support the conversion, the code is
likely to be rejected rather than to misbehave silently.

If you want to get *really* fancy, you can write a function that tests
the byte ordering of some integer type, assume that that corresponds
to the pointer representation, and use that to determine the order in
which to display the bytes. And you can insert, say, a ':' character
between successive bytes to ensure it's unambiguous (or you can check
the value of CHAR_BIT).
 
H

Harald van Dijk

Harald van D?k said:

Well, no pointer value can be printed portably, since the output
associated with %p is implementation-defined. But if you mean there's no
*technique* for printing a function pointer value in ISO C, I must
disagree. (If you simply mean that there's no printf format specifier
for it, then of course you're right.)

#include <stdio.h>

int main(void)
{
int (*mp)(void) = main;
unsigned char *ucp = (unsigned char *)&mp; size_t len = sizeof mp;
while(len--)
{
printf("%02X", *ucp++);
}
puts("");

return 0;
}

<snip>

Your example prints the representation of a function pointer, not its
value. If multiple function pointer representations exist for one value,
there is no portable way to print the value in such a way that the
different representations become indistinguishable.
 
I

Ioannis Vranos

Harald said:
Except for function pointers, which can't be printed portably at all, it
is true. There are no implicit conversions from other pointer types to
void * in the variable arguments of a function, so if any pointer type
would be accepted, how could printf know which one was passed?


In printf() in general, if we want to print the numeric value of a
signed char and of an unsigned char using %d and %u respectively, should
we cast the variable value to the expected type (int and unsigned
respectively)?
 
S

santosh

Ioannis said:
In printf() in general, if we want to print the numeric value of a
signed char and of an unsigned char using %d and %u respectively,
should we cast the variable value to the expected type (int and
unsigned respectively)?

It shouldn't be necessary, at least as far as I can see.
 
M

Micah Cowan

Ioannis Vranos said:
In printf() in general, if we want to print the numeric value of a
signed char and of an unsigned char using %d and %u respectively,
should we cast the variable value to the expected type (int and
unsigned respectively)?

If you're going to use %u for unsigned char, yeah, a cast is a good
idea for maximally correct code. Using %d for either char, signed char
or unsigned char does not require a cast, as it will be promoted to
int in any case.
 
P

Peter Nilsson

Keith Thompson said:
Richard Heathfield said:
#include <stdio.h>

int main(void)
{
  int (*mp)(void) = main;
  unsigned char *ucp = (unsigned char *)&mp;
  size_t len = sizeof mp;
  while(len--)
  {
    printf("%02X", *ucp++);
  }
  puts("");
  return 0;
}

On a system with CHAR_BIT > 8, the above would wor[k],
but it could produce ambiguous results.  For example,
the output "12345" could be result of bytes 0x123 and
0x45 or 0x12 and 0x345.

Then fix it dear Henry. :)

#define CHAR_NIBBLES ((CHAR_BIT + 3)/4)
printf("%0*X", CHAR_NIBBLES, 0u + *ucp++);

Another possible issue with the above is the order in
which it prints the bytes.  On one system, I ran the
above code with an added line:

    printf("main = %p\n", (void*)main);
/* non-portable extension */

and got:

A4830408
main = 0x80483a4

There are always tradeoffs.

Neither is intelligible IMO. But the advantage of
printing bytes is that you _know_ the format (and
width) of the output. That is quite significant in
the hand full of cases where you actually _want_
to print an address!
 
P

Peter Nilsson

Harald van D©¦k said:
... [the] example prints the representation of a function
pointer, not its value.

It's value is determined by the representation.
If multiple function pointer representations exist for
one value, there is no portable way to print the value
in such a way that the different representations become
indistinguishable.

How is this any different to the case for %p? The only
thing you're guaranteed is that if you read via %p you'll
get a pointer that compares equal to the original.

The 'hex dump' approach has the same guarantee.
 
H

Harald van Dijk

Harald van Dijk said:
... [the] example prints the representation of a function pointer, not
its value.

It's value is determined by the representation.

Of course, but that's true for all types. Should we start printing 1.01 as
a series of bytes too, then?
How is this any different to the case for %p? The only thing you're
guaranteed is that if you read via %p you'll get a pointer that compares
equal to the original.

If you print the same value to printf twice, you can be sure you get the
same string twice. If you print the representation of two identical values
twice, you can't be sure you get the same string twice.
 
Y

ymuntyan

Harald van D©¦k said:
... [the] example prints the representation of a function pointer, not
its value.
It's value is determined by the representation.

Of course, but that's true for all types. Should we start printing 1.01 as
a series of bytes too, then?

Isn't it what "binary file formats" is about?
If you print the same value to printf twice, you can be sure you get the
same string twice. If you print the representation of two identical values
twice, you can't be sure you get the same string twice.

You can not be sure that you'll get two identical
strings from printf("%p\n%p\n", p, p). printf() is
"better" only in that scanf() will be able to read
the string back; the "quality" of output may be
"worse" or "better" in either case.

Anyway, it was about function pointers, for which
printf() doesn't work, so...

Yevgen
 
K

Keith Thompson

Peter Nilsson said:
Keith Thompson said:
Richard Heathfield said:
#include <stdio.h>

int main(void)
{
  int (*mp)(void) = main;
  unsigned char *ucp = (unsigned char *)&mp;
  size_t len = sizeof mp;
  while(len--)
  {
    printf("%02X", *ucp++);
  }
  puts("");
  return 0;
}

On a system with CHAR_BIT > 8, the above would wor[k],
but it could produce ambiguous results.  For example,
the output "12345" could be result of bytes 0x123 and
0x45 or 0x12 and 0x345.

Then fix it dear Henry. :)

#define CHAR_NIBBLES ((CHAR_BIT + 3)/4)
printf("%0*X", CHAR_NIBBLES, 0u + *ucp++);

Sure. But using printing hex digits per byte likely *is* the best
format when CHAR_BIT==8.
Neither is intelligible IMO. But the advantage of
printing bytes is that you _know_ the format (and
width) of the output. That is quite significant in
the hand full of cases where you actually _want_
to print an address!

Sure, but using "%p" means that the implementer chooses a format
that's intended to make sense in terms of the addressing structure of
the machine. If I print the addresses of two functions using the
non-portable trick of casting to void* and using "%p", and I and get
"0x80483a4" and "0x80483b8", then that might tell me something about
the relationship between the functions, and it's very likely to match
the information in a link map. If I see "A4830480" and "B8830480",
it's a bit harder to see that relationship, or even to tell whether
the addresses make sense.
 
D

David Tiktin

Harald van D?k said:


Well, no pointer value can be printed portably, since the output
associated with %p is implementation-defined.

I'm a little unclear on the point you're making here. I would have
said that the code:

#include <stdio.h>
#include <limits.h>

int main(void)
{
printf("%d\n", INT_MAX);

return 0;
}

is a completely portable way to print the value of INT_MAX even
though what's printed is obviously implementation defined. I guess I
generally think of "portable" as meaning "will do 'the right thing'
on all platforms" so using %p is the portable way to print non-
function pointers which have been cast to void *. What happens when
you write to a text file is another example. Can't you write to a
text file portably in C even though the result is implementation
defined?

Dave
 
H

Harald van Dijk

... [the] example prints the representation of a function pointer,
not its value.
It's value is determined by the representation.

Of course, but that's true for all types. Should we start printing 1.01
as a series of bytes too, then?

Isn't it what "binary file formats" is about?

I meant printing the series of bytes as hexadecimal numbers, which is not
what binary file formats are about.
You can not be sure that you'll get two identical strings from
printf("%p\n%p\n", p, p).

The standard states that "the value of the pointer is converted to a
sequence of printing characters, in an implementation-defined manner."
This, by my reading, does not allow anything other than the value of the
pointer to have an effect on the output. In particular, it does not allow
the representation of the pointer (when multiple representations of the
same value exist) to have an effect.
 
R

Richard Heathfield

David Tiktin said:
I'm a little unclear on the point you're making here. I would have
said that the code:

#include <stdio.h>
#include <limits.h>

int main(void)
{
printf("%d\n", INT_MAX);

return 0;
}

is a completely portable way to print the value of INT_MAX even
though what's printed is obviously implementation defined.

Right - the technique is portable, but the value isn't. :)

Can't you write to a
text file portably in C even though the result is implementation
defined?

Again, the technique is portable, but the value isn't. Consider:

#include <stdio.h>

int main(void)
{
FILE *fp = fopen("foo.txt", "w");
if(fp != NULL)
{
if(fputs("A\n", fp) != EOF && fclose(fp))
{
puts("All is well.");
}
}
return 0; /* if fputs failed, the stream will be closed on exit! */
}

If "All is well" is displayed (under any conforming hosted implementation),
we have created a text file, which in C terms contains one line comprising
'A' and a newline character. So the technique is portable.

But what have we actually created, in what we might call absolute terms? On
a PC under Windows, the file will contain three bytes, with values 65, 13,
and 10 (obviously I'm using decimal representation here). On an old-style
Mac, it would contain just two bytes: 65, 13. On a Linux box (or, I
*think*, a modern Mac), it would contain 65, 10. On an IBM mainframe,
well, don't ask(!), but to start off with, that 'A' would be 193 rather
than 65. The point is that, whilst the output can be interpreted
consistently within the machine/OS/implementation combination, moving that
file to another (disparate) system will (or at least may) result in that
interpretation becoming invalidated unless some kind of data massage is
performed.

So I guess it all depends on what we mean by "portable"!
 
B

Barry Schwarz

I recall from previous discussions here, that we must cast a non-void
pointer to void * before printing its value with printf("%p"). Is it
true, and if yes why?

Different types of object pointers are allowed to have different sizes
and different representations. But you are allowed to convert
(explicitly or implicitly) between any of these types and void* with
no loss of information.

scanf must accept many different kinds of pointers. %s requires a
char*, %d requires an int*, etc. Therefore, it is not possible to
have a rule that says any pointer in the variadic portion of the
argument list would be "promoted" to void*. (There is such a rule
that says short and char are promoted to int and float is promoted to
double in the variadic portion of an argument list.) [scanf could
have been designed to accept void pointers across the board but then
compilers could not optionally check the arguments against the
conversion specifications.]

Rather than provide printf with a slew of conversion specifications
(which could have also allowed function pointers to be printed), %p is
used for all object pointers after they have been converted to void*.
Since there is no automatic conversion to void* (for the variadic
portion of an argument list), you must direct the compiler to perform
the conversion by casting the value.


Remove del for email
 
P

Peter Nilsson

Barry Schwarz said:
...
scanf must accept many different kinds of pointers.
 %s requires a char*, %d requires an [int], etc.
True.

 Therefore, it is not possible to have a rule that says
any pointer in the variadic portion of the argument
list would be "promoted" to void*.

Why not?

[Not that I'd want to see such a rule.]
 
C

CBFalconer

Peter said:
Barry Schwarz said:
...
scanf must accept many different kinds of pointers.
%s requires a char*, %d requires an [int], etc.
True.

Therefore, it is not possible to have a rule that says
any pointer in the variadic portion of the argument
list would be "promoted" to void*.

Why not?

They SHOULD be promoted to void. The type required is spelled out
by the format string, so they can always be converted back to the
appropriate form. This is one reason why the format string has to
agree with the types received. All scanf needs to know is in the
format, and in the void* pointer.
 
B

Barry Schwarz

Barry Schwarz said:
...
scanf must accept many different kinds of pointers.
 %s requires a char*, %d requires an [int], etc.

Interesting that your reader didn't quote what I wrote.
True.


Why not?

Because then scanf would not be receiving the expected type. Changing
scanf so this could be done was mentioned two sentences later.
[Not that I'd want to see such a rule.]


Remove del for email
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top