8

First off, this is not a dupe of:

Is it safe to cast an int to void pointer and back to int again?

The difference in the questions is this: I'm only using the void* to store the int, but I never actually use it as a void*.

So the question really comes down to this:

Is a void * guaranteed to be at least as wide as an int

I can't use intptr_t because I'm using c89 / ANSI C.

EDIT

In stdint.h from C99 ( gcc version ) I see the following:

/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int        intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned long int   uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int         intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned int        uintptr_t;
#endif

Could I possibly just jerry rig something similar and expect it to work? It would seem that the casting should work as all intptr_t is is a typedef to an integral type...

Community
  • 1
  • 1
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • 2
    Why do you need to store an `int` in a `void *`? – Oliver Charlesworth Aug 12 '11 at 15:26
  • @Oli Charlesworth: I wanted to build a flexible array "class" which could handle both pointers to objects and raw intrinsic types like int. I thought about trying to use a union to force enough storage space, but the problem is the casting might screw things up in unexpected ways. I know I can get away with it on modern x86, but I'd like it to be portable. – Robert S. Barnes Aug 12 '11 at 15:57
  • What casting? Are you talking about `union { int i; void *p; }`? – Oliver Charlesworth Aug 12 '11 at 16:05
  • @Oli Charlesworth: Internally I might have a union, but the interface would be something like int appendData( void * data ); – Robert S. Barnes Aug 12 '11 at 16:16
  • Why don't you store your data and point to it with a `void*`? Storing something _inside_ a `void*` makes hardly sense. – pmr Aug 12 '11 at 16:23
  • Provide two functions: `appendData(void *data)` + `appendInt(int n)`, and use a union internally. Obviously the caller knows whether they are dealing with pointers or primitive types, so it is no burden to call different functions to append the different types. Requiring the _caller_ to explicitly cast is a fairly reliable symptom of a bad interface, anyway... – Nemo Aug 12 '11 at 16:28
  • @Nemo: That's what I'm ending up doing, but I'm still curious about the answer to my question. – Robert S. Barnes Aug 13 '11 at 18:30

4 Answers4

9

No, this is not guaranteed to be safe.

The C99 standard has this to say (section 6.3.2.3):

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.

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

I'm pretty confident that pre-C99 won't be any different.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I'm curious regarding the "Except as previously specified" part. I tried looking for something relevant and couldn't find it in a few minutes... – Robert S. Barnes Aug 13 '11 at 18:34
  • @Robert: The integer constant 0 converting to a null pointer contant is immediately preceding and that was probably your blind spot. – u0b34a0f6ae Nov 03 '11 at 11:26
2

FreeRTOS stores timer IDs in Timer_t as void* pvTimerID. So when using this as a storage space, and NOT a pointer to something, it is necessary to cast it to something that can be used as an array index, for instance.

so to read the id, stored as a void*:

void* pvId = pxTimer->pvTimerID; int index = (int)(pvId - NULL);

1

There is a C FAQ: Can I temporarily stuff an integer into a pointer, or vice versa? .

The cleanest answer is: no, this is not safe, avoid it and get on with it. But POSIX requires this to be possible. So it is safe on POSIX-compliant systems.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
1

Here's a portable alternative.

static const char dummy[MAX_VALUE_NEEDED];
void *p = (void *)(dummy+i); /* cast to remove the const qualifier */
int i = p-dummy;

Of course it can waste prohibitively large amounts of virtual address space if you need large values, but if you just want to pass small integers, it's a 100% portable and clean way to store integer values in void *.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711