Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
502 views
in Technique[技术] by (71.8m points)

c - Cast int to pointer - why cast to long first? (as in p = (void*) 42; )

In the GLib documentation, there is a chapter on type conversion macros. In the discussion on converting an int to a void* pointer it says (emphasis mine):

Naively, you might try this, but it's incorrect:

gpointer p;
int i;
p = (void*) 42;
i = (int) p;

Again, that example was not correct, don't copy it. The problem is that on some systems you need to do this:

gpointer p;
int i;
p = (void*) (long) 42;
i = (int) (long) p;

(source: GLib Reference Manual for GLib 2.39.92, chapter Type Conversion Macros ).

Why is that cast to long necessary?

Should any required widening of the int not happen automatically as part of the cast to a pointer?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The glib documentation is wrong, both for their (freely chosen) example, and in general.

gpointer p;
int i;
p = (void*) 42;
i = (int) p;

and

gpointer p;
int i;
p = (void*) (long) 42;
i = (int) (long) p;

will both lead to identical values of i and p on all conforming c implementations.
The example is poorly chosen, because 42 is guaranteed to be representable by int and long (C11 draft standard n157: 5.2.4.2.1 Sizes of integer types ).

A more illustrative (and testable) example would be

int f(int x)
{
  void *p = (void*) x;
  int r = (int)p;
  return r;
}

This will round-trip the int-value iff void* can represent every value that int can, which practically means sizeof(int) <= sizeof(void*) (theoretically: padding bits, yadda, yadda, doesn't actually matter). For other integer types, same problem, same actual rule (sizeof(integer_type) <= sizeof(void*)).

Conversely, the real problem, properly illustrated:

void *p(void *x)
{
  char c = (char)x;
  void *r = (void*)c;
  return r;
}

Wow, that can't possibly work, right? (actually, it might). In order to round-trip a pointer (which software has done unnecessarily for a long time), you also have to ensure that the integer type you round-trip through can unambiguously represent every possible value of the pointer type.

Historically, much software was written by monkeys that assumed that pointers could round-trip through int, possibly because of K&R c's implicit int-"feature" and lots of people forgetting to #include <stdlib.h> and then casting the result of malloc() to a pointer type, thus accidentally roundtripping through int. On the machines the code was developed for sizeof(int) == sizeof(void*), so this worked. When the switch to 64-bit machines, with 64-bit addresses (pointers) happened, a lot of software expected two mutually exclusive things:

1) int is a 32-bit 2's complement integer (typically also expecting signed overflow to wrap around)
2) sizeof(int) == sizeof(void*)

Some systems (cough Windows cough) also assumed sizeof(long) == sizeof(int), most others had 64-bit long.

Consequently, on most systems, changing the round-tripping intermediate integer type to long fixed the (unnecessarily broken) code:

void *p(void *x)
{
  long l = (long)x;
  void *r = (void*)l;
  return r;
}

except of course, on Windows. On the plus side, for most non-Windows (and non 16-bit) systems sizeof(long) == sizeof(void*) is true, so the round-trip works both ways.

So:

  • the example is wrong
  • the type chosen to guarantee round-trip doesn't guarantee round-trip

Of course, the c standard has a (naturally standard-conforming) solution in intptr_t/uintptr_t (C11 draft standard n1570: 7.20.1.4 Integer types capable of holding object pointers), which are specified to guarantee the
pointer -> integer type -> pointer
round-trip (though not the reverse).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...