views:

242

answers:

7

Rather than sending an actual pointer to a value, the value is cast to a pointer. I found these examples in the GUI interface code of a GTK program.

g_signal_connect (pastebutton[pane],
                  "clicked",
                  G_CALLBACK(on_paste_button_pressed),
                  (void*)((long)pane<<4));

In the above example, I am referring to the last parameter of g_signal_connect. When on_paste_button_pressed is called by GTK2, on_paste_button_pressed casts the user_data void pointer back like so:

int pane = ((long)user_data) >> 4;

Actually, I added this particular example to the code, but I based it upon what was already there. I added the bit-shifting so as to avoid warnings about casting. The program itself has four panes containing quite a number of widgets, the copy and paste buttons allow you to copy all the values from one pane to another.

Is this way of casting a value to a pointer address often used, and are there reasons why this should not be used?

edit:

The cast from an integer to a void pointer can also be achieved like so:

void* void_ptr = some_int - NULL;
A: 

It is used but it's by no means portable so you have to be careful.

The C standard does not mandate that pointer types have at least as many bits as integer types, so you may not always be able to do it.

But I can't recall any platform in which pointers have actually been smaller than integers so you're probably safe (despite not being technically safe).

The only reason I can think that the casting may be there for is to remove the possibility of alignment warnings. Section 6.3.2.3 of the C1x draft states:

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

paxdiablo
Both `6.3.2.3/5` and `/6` refer to something "previously specified" when they talk about conversions between pointers and integers. I can't figure out what it is. What relevant was "previosuly specified" in thsi case?
AndreyT
@AndreyT, I'm pretty certain they mean what's been specified already in 6.3.2.3/1-4 (how pointers are converted in a standards-defined manner rather than an implementation-defined manner). That's usually the case when the standards include language like that.
paxdiablo
@paxdiablo: I guess they are referring to `/3`, which is the only previous part that appears to be relevant to pointer<->integer conversions. I.e. the literal 0 -> null-pointer value part.
AndreyT
A: 

Casting an integer to a pointer is used to pass a value by-value. This is the preferred way if you do need a by-reference parameter because the compiler then does not need to dereference the pointer.

The bit-shifting is a bad idea because it can cause overflows.

For really portable code, you should use intptr_t as your integer type cause it will nicely fit into a pointer.

Tomas
A: 

It does see use in these kinds of cases, yes. It works on many platforms, but might fail because an arbitrary integer is not always a valid pointer value, though the shifting you do should get around that. It is also possible that a pointer cannot hold all the values that an integer can; this would be the case if you're using a 64-bit long on a platform where pointers are 32 bits, and of course since you are shifting the value, it could also fail even if pointers and integers are the same size. If you want to use this trick, you should probably check sizeof (void*) against the size of the integer type you use, and at runtime check against the actual value if the pointer isn't big enough. It's probably not worth it to make this fully portable so that you would use an actual pointer on platforms where that's needed, so either limit yourself to platforms where this trick works or abandon the trick altogether.

jk
A: 

I find it just fine. Don't have statistics about use frequency, but do you really care?

I'm not sure I understand how bit-shifting helps, though.

Michael Krelin - hacker
No I don't really care about statiscs :-D
James Morris
+3  A: 

It is used. It is used quite commonly when it does what's required.

One reason not to use it is that theoretically pointer size might be smaller than the source integer size.

Another reason not to use it is that it allows you to pass only one piece of integer data to the callback. If in the future you'll need to add another piece of data (or switch to a non-integer), you'll have to locate and rewrite every single place where the callback makes access to the passed data. So, if there's a chance that you'd have to extend the data in the future, it is better to create a struct (even if it holds just a single int at this time) and pass a pointer to that struct.

But if you are sure that you'll never have to pass anything other than that single integer and that your integer fits into a void *, then this technique is not in any way broken.

P.S. Pedantically speaking, neither C nor C++ appear to have the roundtrip guarantee for integer-to-void *-to-integer conversion, i.e. they don't guarantee that it will work and restore the original integral value.

AndreyT
They also don't guarantee that the pointer value you get when you cast doesn't cause an instantaneous bad pointer trap. I have never seen this in reality, though!
caf
A: 

This is from the C-FAQ:

Q: How are integers converted to and from pointers? Can I temporarily stuff an integer into a pointer, or vice versa?

A: Once upon a time, it was guaranteed that a pointer could be converted to an integer (though one never knew whether an int or a long might be required), and that an integer could be converted to a pointer, and that a pointer remained unchanged when converted to a (large enough) integer and back again, and that the conversions (and any mapping) were intended to be ``unsurprising to those who know the addressing structure of the machine.'' In other words, there is some precedent and support for integer/pointer conversions, but they have always been machine dependent, and hence nonportable. Explicit casts have always been required (though early compilers rarely complained if you left them out).

The ANSI/ISO C Standard, in order to ensure that C is widely implementable, has weakened those earlier guarantees. Pointer-to-integer and integer-to-pointer conversions are implementation-defined (see question 11.33), and there is no longer any guarantee that pointers can be converted to integers and back, without change.

Forcing pointers into integers, or integers into pointers, has never been good practice. When you need a generic slot that can hold either kind of data, a union is a much better idea.

See also questions 4.15, 5.18, and 19.25.

References: K&R1 Sec. A14.4 p. 210 K&R2 Sec. A6.6 p. 199 ISO Sec. 6.3.4 Rationale Sec. 3.3.4 H&S Sec. 6.2.3 p. 170, Sec. 6.2.7 pp. 171-2

Robert S. Barnes
+2  A: 

You should use macros GINT_TO_POINTER() and GPOINTER_TO_INT() to cast between pointers and integers.

glib: Type Conversion Macros

el.pescado
Excellent, cheers!
James Morris
I don't see how it is even remotely relevant in this case. The source type is fixed by the context (some integer), the destination type is forced by the callback (void *). If that integer can be stuffed into a `void *`, the original code will work. If it can't be stuffed into a `void *` for some reason, then no glib macro will ever help you.
AndreyT
The macros make the code clearer. For what is required, they are fine.
James Morris
I had accepted this as the correct answer, but decided not to as it does not actually answer the question, but I shall leave the +1 I gave.
James Morris