Question

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;
Was it helpful?

Solution

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.

OTHER TIPS

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

glib: Type Conversion Macros

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.

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.

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.

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top