Typecasting in C

A

Arthur J. O'Dwyer

The expression
printf("the address is: 0x%x\n",p);
where p is some pointer appears in several million lines in
existing code.

Has anyone besides Jacob ever seen this construct (or anything
similar) in real-world code? I've never encountered it. The
usual idiom in my part of the universe is more like

printf("(%d) %p\n", p!=NULL, (void*)p);

which has the benefit of (a) telling you something useful, and
(b) being standard, warning-less C code.

Who's been using "%d" or "%x" to print *pointer* values?

-Arthur
 
A

Alex Monjushko

Has anyone besides Jacob ever seen this construct (or anything
similar) in real-world code? I've never encountered it. The
usual idiom in my part of the universe is more like
printf("(%d) %p\n", p!=NULL, (void*)p);
which has the benefit of (a) telling you something useful, and
(b) being standard, warning-less C code.
Who's been using "%d" or "%x" to print *pointer* values?

It is quite common in legacy code. Especially in the stuff that
got carried over from the pre-ANSI days.
 
H

Harti Brandt

On Tue, 29 Jun 2004, jacob navia wrote:

jn>The expression
jn>
jn>printf("the address is: 0x%x\n",p);
jn>
jn>where p is some pointer appears in several million lines in
jn>existing code.
jn>
jn>The warnings can become a nuisance and people would stop
jn>using this feature. Personally I think warnings should be
jn>kept to the essential ones, warnings that would uncover a
jn>possible error.
jn>
jn>Strictly speaking you should use %p, but I have almost
jn>never seen it in debugging code, where this conversion is
jn>used.
jn>
jn>To the contrary of your expectations, I work to make a usable
jn>compiler, not one that will please the purists around c.l.c

That has nothing to do with purism. Ever cared to work on a machine where
sizeof(void *) > sizeof(int)? The nearest to correct thing to do if you
happen to have a printf() without %p would be to use %lx and cast the
pointer to an unsigned long.

harti
 
K

Keith Thompson

jacob navia said:
The expression

printf("the address is: 0x%x\n",p);

where p is some pointer appears in several million lines in
existing code.

Then there are several million errors, and you'd be doing your users a
favor by letting them know.

For example, on an IA-64 system:

#include <stdio.h>
int main(void)
{
char *p = "hello";
printf("Using %%x: p = 0x%x\n", p); /* wrong */
printf("Using %%p: p = %p\n", (void*)p); /* right */
return 0;
}

produces the following output:

Using %x: p = 0x7c0
Using %p: p = 0x40000000000007c0

I suspect that platforms with 32-bit ints and 64-bit pointers are
going to become much more common over the next few years. The sooner
programmers realize that all the world's not a VAX^H^H^H x86, the
better. Even if the sizes happen to match, it's still undefined
behavior, and any number of things can go wrong.

If you really want to make sure the address value is displayed in
hexadecimal, and you're willing to assume that pointers and ints are
the same size (e.g., you're writing a quick-and-dirty debugging
statement that will be deleted before the code could ever be ported to
another platform), you can always cast the pointer value to unsigned
int to inhibit the warning. The result may or may not be meaningful,
but at least it avoids undefined behavior.

Or you can just use "%p", which exists for exactly this purpose.
 
R

Randy Howard

Has anyone besides Jacob ever seen this construct (or anything
similar) in real-world code? I've never encountered it.

Unfortunately yes, but not in quite some time. I ran into it
fairly often in the MS-DOS and early Windows days. (Maybe Mr.
Schildt used it a lot in his books???) It was probably in a fair
amount of older legacy UNIX code as well, but I can't recall an
example offhand.

Usually they at least cast the type of p to something appropriate
for the %d or %x on the platform. Every time I encounter something
like that, I fix it, but it doesn't happen much anymore, plus I very
rarely get stuck maintaining code that poor these days.
 
R

Randy Howard

To the contrary of your expectations, I work to make a usable
compiler, not one that will please the purists around c.l.c

Jacob, if you ever (or already) support 64-bit AMD Opteron/AthlonFX or the
Intel clones of same, you're users are going to be in for a rude awakening
when they get to work porting. Yes, it may be so common on 32-bit platforms
for programmers to make assumptions that it *seems* like you're doing them
a favor to ignore it, in the long run, when people WILL be porting a lot
of legacy code to new platforms, it will be a hindrance, not a benefit.
 
R

Randy Howard

In the next implementation of lcc-win32 that will be the case (64 bit
machine).

In THAT environment I will issue the warning for all pointer/int
conversions.

So people using your 32-bit compiler to develop code which will migrate
to 64 in the future will have to wait until later to be warned of possible
errors in their program. That's not a good idea.
In an environment where sizeof(int)==sizeof(void*) many people
(including me) write printf("pointer value=%#x\n",p); to debug
a piece of code.

You write this in new code? Why? I could understand you trying to
support legacy code through some sort of "shut up warnings about this"
mode, but not by default, and certainly don't understand writing new
code with a modern compiler as you describe.
The compiler spitting you hundreds of warnings
would only have the consequence of people IGNORING all warnings.

Strange, I try very hard to get my code to compile (on multiple platforms,
with varying compilers) with no warnings at all, using the highest warning
level on each compiler. It's not always possible, but I try very hard to
do so without bastardizing the source to get there. I have met programmers
that did ignore all warnings, but they were not around long enough in any
case to have a lasting impact.
It is a pity that people here like to have an atmosphere of
aggresivity that is very boring.

I didn't mean to be aggressive earlier, I think it was more *shock* than
animosity. I would have expected this discussion 10 years ago here, not
today, especially in light of the age of the standards that allow it to
be avoided, not to mention the 64-bit transitions currently very active
in a lot of places.
 
J

jacob navia

Keith Thompson said:
Then there are several million errors, and you'd be doing your users a
favor by letting them know.

If they set the debug level to high then yes. If not, the compiler
accepts it. I think this discussion has shown me that
maybe a warning is needed, if the programmer wishes.

lcc-win32 (as its name implies) is a 32 bit system. I am working
in the 64 bit version already, but that is another topic.
For example, on an IA-64 system:

#include <stdio.h>
int main(void)
{
char *p = "hello";
printf("Using %%x: p = 0x%x\n", p); /* wrong */
printf("Using %%p: p = %p\n", (void*)p); /* right */
return 0;
}

produces the following output:

Using %x: p = 0x7c0
Using %p: p = 0x40000000000007c0

Yes, this is one of the reasons I think a warning should be
issued if the programmer wishes. It helps porting code
from 32-->64 bits.
I suspect that platforms with 32-bit ints and 64-bit pointers are
going to become much more common over the next few years. The sooner
programmers realize that all the world's not a VAX^H^H^H x86, the
better. Even if the sizes happen to match, it's still undefined
behavior, and any number of things can go wrong.

Yes. I started porting the code and it is hard. Warnings like this
could improve the situation. In any case in the 64 bit system a warning
will be issued since sizeof(int) != sizeof(void *).
If you really want to make sure the address value is displayed in
hexadecimal, and you're willing to assume that pointers and ints are
the same size (e.g., you're writing a quick-and-dirty debugging
statement that will be deleted before the code could ever be ported to
another platform), you can always cast the pointer value to unsigned
int to inhibit the warning.

But this is just effort for throwaway code. Why bother?

Most of those printf statements are for situations where the
debugger is absent.

I started programming when this statements were the only
way of debugging since there wasn't any debugger.

And I wrote (to see if a pointer had a value) %x and was done
with it.

This has worked since a long time. Yes, I know about
%p, but I didn't bother. Why? It continued to work.
The result may or may not be meaningful,
but at least it avoids undefined behavior.

Or you can just use "%p", which exists for exactly this purpose.

Yes, I think I will be forced to write that in the 64 bit system.

I am wary of forcing casts to please the compiler... In a 32
bit system where sizeof void * is the same as sizeof int, this
warning has no sense really, and should be optional.

Do not clutter output. Filter it. Make more verbose
options available but choose a sensible default:

do not clutter...
 
H

Harti Brandt

On Tue, 29 Jun 2004, Arthur J. O'Dwyer wrote:

AJO>
AJO>On Tue, 29 Jun 2004, jacob navia wrote:
AJO>>
AJO>> The expression
AJO>> printf("the address is: 0x%x\n",p);
AJO>> where p is some pointer appears in several million lines in
AJO>> existing code.
AJO>
AJO> Has anyone besides Jacob ever seen this construct (or anything
AJO>similar) in real-world code? I've never encountered it. The
AJO>usual idiom in my part of the universe is more like
AJO>
AJO> printf("(%d) %p\n", p!=NULL, (void*)p);
AJO>
AJO>which has the benefit of (a) telling you something useful, and
AJO>(b) being standard, warning-less C code.
AJO>
AJO> Who's been using "%d" or "%x" to print *pointer* values?

%p is a quite new feature for printf(). Neither V7 nor BSD had this, so
the natural way of printing pointers was %x. Don't assume that everybody
out there does a daily update of it's compilers and libraries to the
current gcc.

harti
 
J

jacob navia

Randy Howard said:
Jacob, if you ever (or already) support 64-bit AMD Opteron/AthlonFX or the
Intel clones of same, you're users are going to be in for a rude awakening
when they get to work porting. Yes, it may be so common on 32-bit platforms
for programmers to make assumptions that it *seems* like you're doing them
a favor to ignore it, in the long run, when people WILL be porting a lot
of legacy code to new platforms, it will be a hindrance, not a benefit.

Yes, I added an optional warning. If you want this (and other
warnings) you set higher warning level.

64 bit systems were never a succes, since at least a few
years. Many of them started but never become popular.

And I am sure that issues like %x or %p will be the least
of the problems I will face. Data size bloat, code size bloat...
64 bits is very expensive...

Need more than 4GB of memory recently?

If we bloat things that doesn't make them faster. Twice as
much data needs to be loaded, read-in, etc. This
doesn't make for very significant improvements. But this
is another topic.
 
K

Kenneth Brody

:
[...]
printf("the address is: 0x%x\n",p);
[...]
printf("(%d) %p\n", p!=NULL, (void*)p); [...]
Who's been using "%d" or "%x" to print *pointer* values?

I have old legacy code, from before "%p" existed, with a similar construct.
However, it uses "%lx" and the cast "(long)ptr". This at least "works" on
systems where pointers are not bigger than longs. (Which is all platforms
for which it is currently ported to.)
 
D

Default User

Harti said:
%p is a quite new feature for printf().

You must have an interesting definition of "quite new" in that it
appeared in the 1989 standard and K&R 2.
Neither V7 nor BSD had this, so
the natural way of printing pointers was %x. Don't assume that everybody
out there does a daily update of it's compilers and libraries to the
current gcc.

One needn't have a daily update, just one from, oh, the last 10 years or
so.




Brian Rodenborn
 
O

Old Wolf

jacob navia said:
I pondered a long time about that one, since it is perfectly legal to
do:

char *p;
...

printf("The address is %d\n",p);

No it isn't. Putting anything other than an int, or a type that promotes
to int, is undefined. (Having said that, you can of course make
your compiler define that behaviour. But this code is not portable
to other compilers).
specially in debugging code.

I always write standard code for debugging. Why take the risk
of having your debugging information come out wrong because you
did something undefined? When you're frustrated with some hard-to-find
bug, the last thing you need is the debugging to not work.
Granted, this is weird, but how to discriminate between
legal and wrong usage?

Legal usage adheres to the standard, wrong usage doesn't.
But maybe you are right. I added a warning when the "d" format
is used with a pointer.

The "x" format will NOT provoke any warnings.

Do you not know about %p ?
But this is at the limit of what a compiler can do.

This is all very much simpler than, say, writing a C++ compiler.
I'm sure you could even build a runtime check to match the
format string to the types of the arguments.
 
J

jacob navia

Default User said:
You must have an interesting definition of "quite new" in that it
appeared in the 1989 standard and K&R 2.


One needn't have a daily update, just one from, oh, the last 10 years or
so.

As I mentioned in another thread, I started learning C using those printf
statements for debugging since there wasn't any debugger in those times.

And the usage became an habit. It has been working since
more or less 20 years.

Is that a terrible sin?

Do not know. I am not a star programmer, neither the one
that never makes mistakes sorry.

Those printf statements never survived a lot anyway. Why
should I bother?

Is this extremely important?

Maybe. The fact that I check printf (as few compilers do
actually) is ignored, and an oversight is amplified.
 
R

Randy Howard

Yes, I added an optional warning. If you want this (and other
warnings) you set higher warning level.

If your compiler supports the logical equivalent of gcc's -pedantic
it should be there by default. Perhaps also for whatever might
equate to -std=c89 or -std=c99.
64 bit systems were never a succes, since at least a few
years. Many of them started but never become popular.

I guess all of those new macintoshes I see people buying are a
failure. Apparently the very high volume of 64-bit AMD servers,
desktops and even notebooks now being sold are also a failure.
If you said "Itanium" I would be in agreement. In the first
year of general availability, x86_64 processors outsold Itanium
about 8:1, despite being to market 4 or 5 years later. 64-bit
is coming, and quickly, particularly on the server side, but also
the gamer kids are jumping on the bandwagon.

I have a 64-bit AMD notebook now, and it's wonderful, even when
running 32-bit windoze, but far more fun with SuSE 9.1 64-bit.
And I am sure that issues like %x or %p will be the least
of the problems I will face.

True. A lot of programmers, especially maintaining large WIN32
apps are having a hell of a time moving, because of all of the
MS data type proliferations that make really painful assumptions
about the sizes of things. If however you stick to the "portable
C sandbox", it's very, very painless. YMMV.
Data size bloat

Not necessarily, unsigned char's are still available. :) So
are most of the other "smaller" data types. Fortunately, limits.h
has been around for a while, which helps considerably in this area.
code size bloat...

Minor in the grand scheme of things, but yes...
64 bits is very expensive...

Actually, 64-bit Opteron servers typically sell for about 1/2 of
a slower, less capable Xeon server. :) (I know what you meant)
Need more than 4GB of memory recently?

Actually, yes. Not so much for myself, but because I needed to
write some code to stress memory above 4GB on IA-32 systems
that supported it via PAE. Not pretty. On 64-bit platforms,
it's unlikely I'll be around long enough to see a machine with
enough memory sticks to fill up the address space.

More typically, database programmers have been fighting this
problem for quite a while. If you really want to see an ugly
hack to get around this problem, go search for "AWE" and memory
on the MSDN website. Not pretty, but in a sense unavoidable
to get around 4GB on a 32-bit box.

More concretely, it's a daily issue for server development and
end users, where 4GB limitations on file sizes really suck when
you have a 2TB filesystem at your disposal, thanks to 32-bit
file offset issues. There are hacks to get around the problem of
course. The point is, it's not just about how much memory you have
installed.
If we bloat things that doesn't make them faster.

Taken at face value, that's true. It doesn't mean that a 64-bit
platform and software is automatically bloated without any
redeeming benefits. A fair amount of crypto code for example
can be shown to be much, much more efficient on 64-bit platforms,
but it's also true for quite a few other things. There are also
some major architectural benefits with the AMD 64-bit technology
such as Hypertransport, which flat out slay Intel FSB memory
designs on throughput, particularly in SMP implementation, which is
a big improvement as processor speed increases continue to outpace
memory performance in general.
Twice as much data needs to be loaded, read-in, etc.

And 64-bit designs are much better at loading in twice as much
data than 32-bit designs, pretty much across the board.

You might consider that people said almost the same thing (and
were proved wrong fairly quickly) when the 8 -> 16-bit transitions
and the 16 -> 32-bit transitions happened. No sense fighting
progress.
This doesn't make for very significant improvements.

Actually, it does, but it sounds as if you haven't had much experience
working on various 64-bit platforms yet, so it doesn't make much
sense to continue this until some later date when you have.

The main point still is that a compiler should support portability of code
compiled with it, not believe, wish, or otherwise pretend that it won't
happen.
 
J

jacob navia

Randy Howard said:
but it sounds as if you haven't had much experience
working on various 64-bit platforms yet, so it doesn't make much
sense to continue this until some later date when you have.

I ported lcc-win32 to windows XP 64 bits.

Rewrote the assembler, the linker, adapted the debugger.

It is working, in an embryonic stage yet.

sizeof(void *) == 64, sizeof(int) == 32.

The best thing is not the 64 bit but *the EXTRA REGISTERS!!!*

8 new registers at last... The Intel architecture didn't saw that
for 20 years...

I think lcc-win32 will eventually migrate to

sizeof(void *) == 32, sizeof(int) 32
sizeof( __long void *) == 64, sizeof (long long) == 64.

For the few data items where you may need more than
4GB of addressing space a special pointer type would be more efficient
than putting all pointers in 64 bit carrying mostly
32 bits of zeroes around.

There is no free lunch. The complexity of having
two pointer types can become an obstacle.

I haven't decided yet which way to go.
 
C

Christian Bau

"jacob navia said:
I think lcc-win32 will eventually migrate to

sizeof(void *) == 32, sizeof(int) 32
sizeof( __long void *) == 64, sizeof (long long) == 64.

Are you sure about that?
 
J

jacob navia

Christian Bau said:
Are you sure about that?

The rationale is that most programs do have a certain use of the
extra registers provided by the new architecture, but will never
need more than 4GB address range for most pointers.

Since 32 bit isntructions are smaller and the size of the
pointers in structures doesn't change, it becomes an
easy way to migrate the existing software base to 64 bits
without a lot of pain.

Nothing changes, you just have a new "long"
pointer type when you need it.

In the moment where the complexity of two pointers
types outweights the performance benefits this is
moot.

But I do not have implemented that yet. It is
a lot of work, much more than just following the
trend and using 64 bit pointers.
 
D

Default User

jacob said:
As I mentioned in another thread, I started learning C using those printf
statements for debugging since there wasn't any debugger in those times.

And the usage became an habit. It has been working since
more or less 20 years.

Is that a terrible sin?

As my reply was not to you, but to someone who maintained that %p was
some new invention, I don't know why you are whining to me. I don't give
a good goddamn why you choose not to use what the language provides.



Brian Rodenborn
 
K

Keith Thompson

jacob navia said:
lcc-win32 has several levels of warnings. After this discussion I have added
a warning when the warning level is higher than normal for all
implicit pointer/int conversions in the printf formats.

It's not really an implicit conversion. At best, it's a pointer
representation being interpreted as if it were an integer. The effect
is often identical, but it's best not to think of it as a "conversion".

Consider an analagous call:
printf("f = %d\n", f);
where f is of type float. There is no conversion from float to int,
just a float representation being interpreted as an int value. The
displayed value will most likely be garbage.

It happens that conversions between floats and integers typically have
to do an actual conversion to get the right value, while conversions
between pointers and integers typically just copy the bits. That's
why
printf("f = %d\n", some_float_value);
typically prints garbage, while
printf("p = 0x%x\n", some_pointer_value);
often gives you something that looks meaningful, but both are equally
undefined behavior, and both will fail badly on some real-world
systems.

I urge you to issue a warning for both cases at the default warning
level; if you want to provide some mechanism to inhibit certain
warnings, that's fine. (This is my opinion; the standard, of course,
doesn't require a warning in either case.) I also urge you to use "%p"
in your own code.
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top