Storing integer values in a void*

L

lithiumcat

Hello,

I'm using some functions to store and retrieve data as a void*. They
happen to be an AVL-tree implementation, but that's irrelevant for my
question, so let's just think of a black box that handles void*.

I think there is no problem as long as I use pointers to data
allocated elsewhere, because the standard says that a pointer to
whatever, converted to a void*, and converted back to the same
whatever, gives the same pointer.

But I want to use that code to store small integers (maybe not more
than a thousand, but I don't want to hardcode a limit), and I'm
wondering whtat is the best portable way to do it.

I guess the most natural way to use these functions would be to malloc
space for a single int, and use the returned pointer as a void* (with
implicit conversion when calling the storing function). Something like
that :
/* storage */
int *i = malloc(sizeof *i);
*i = value_to_be_stored;
store_data_as_void_ptr(i);
/* retrieval */
int *i = get_data_returned_as_void_ptr();
/* use *i */

However this does not look very efficient in terms of memory, because
of the malloc() overhead for every integer, plus the memory used for
the void* in the blackbox.

I thought that because void* is basically a pointer, and therefore a
numeric value, it might be possible to store the value directly into
the pointer value. I guess something like the following would work on
some architectures :
/* storage */
int i = value_to_be_stored;
void *data = (void *)i;
store_data_as_void_ptr(data);
/* retrieval */
int i;
void *data = get_data_returned_as_void_ptr();
i = (int)data;
/* use i */

However I'm not sure any standard guarantees this to work, so I was
looking for a more portable solution. I thought doing some pointer
arithmetics :
/* global variable to be used for base of pointers */
char base;
/* storage */
int i = value_to_be_stored;
char *data = &base + i;
store_data_as_voir_ptr(data);
/* retrieval */
int i;
char *data = get_data_returned_as_void_ptr();
i = data - &base;
/* use i */

But is this really portable (assuming i is small enough so there is no
overflow) ? I guess the pointers will probably be invalid, but as they
are never dereferenced, it is alright, isn't it ?

Have I made a mistake in all this ? Is there a better way to handle my
problem ?

Thanks in advanced for your ideas.
Natacha
 
S

Sebastian Morawietz

Hi there,

how about using a union and a type variable, instead of just a
pointer?

Hope this helps

Seb

-------------------

#include <stdio.h>
#include <stdlib.h>


#define VALUE_TYPE_POINTER (1)
#define VALUE_TYPE_FLOAT (2)
#define VALUE_TYPE_INT (3)

typedef int value_type_t;

typedef struct node_t
{
value_type_t type;
union {
int intValue;
float floatValue;
void* pointerValue;
} value;
} node_t;

node_t*
node_new_with_pointer( void* value )
{
node_t* result = calloc( 1, sizeof( node_t ) );

result->type = VALUE_TYPE_POINTER;
result->value.pointerValue=value;

return result;
}

node_t*
node_new_with_int( int value )
{
node_t* result = calloc( 1, sizeof( node_t ) );

result->type = VALUE_TYPE_INT;
result->value.intValue=value;

return result;
}

int
main()
{
node_t* node = node_new_with_int( 17 );

printf( "node is an int type: %s\nValue: %i\n",
node->type == VALUE_TYPE_INT ? "true" : "false",
node->value.intValue );
free(node );

char* test="blahblah";
node=node_new_with_pointer( test );

printf( "node is a pointer-type: %s\n"
"Pointer is equal to test: %s\n",
node->type == VALUE_TYPE_POINTER ? "true" : "false",
test == node->value.pointerValue ? "true" : "false" );

free( node );

return 0;
}
 
B

Ben Bacarisse

I'm using some functions to store and retrieve data as a void*.
But I want to use that code to store small integers (maybe not more
than a thousand, but I don't want to hardcode a limit)
However I'm not sure any standard guarantees this to work,

No, you are right. It is not guaranteed to work. It will work in
lots of cases, but it is not portable.
so I was
looking for a more portable solution. I thought doing some pointer
arithmetics :
/* global variable to be used for base of pointers */
char base;
/* storage */
int i = value_to_be_stored;
char *data = &base + i;
store_data_as_voir_ptr(data);
/* retrieval */
int i;
char *data = get_data_returned_as_void_ptr();
i = data - &base;
/* use i */

But is this really portable (assuming i is small enough so there is no
overflow) ? I guess the pointers will probably be invalid, but as they
are never dereferenced, it is alright, isn't it ?

Clever but, no, not portable. Even constructing an address outside of
and object (except for the off-by-one-at-the-end permission) is
undefined.

The only portable solution that I can think of is the union idea
already posted. This, of course, requires changes to the AVL tree
code, so it may not be an option.
 

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,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top