-1

I found Converting double to void* in C but i cant get none of the solutions to run. I want to create a struct with a void pointer to store different types of data.

struct Table {
    int id;
    void *val;
}; 

In the main function i assign the values like

struct Table t;

int i = 1;
double d = 2.5;

double *pd = &d;
void **p = (void**)pd;
void *dp = *p;

t.id = i;
t.val = dp;

but i cant get the value of the double..

printf("%d %f", t.id, t.val);

Thank you.

  • 2
    You *do not* store a `double` in a `void *`. You may, however, store a *pointer to `double`*, a.k.a. a `double *` in an object of type `void *`. – John Bollinger Feb 11 '19 at 17:26
  • 2
    You can set `t.val = &d;` or `t.val = pd;` to point `t.val` to the `double`. Note, this is converting a `double *` to a `void *`, not storing a `double` in a `void *` as your question title implies. Be careful to make sure the pointer is still pointing to something valid when you access it. To access the `double`, convert `t.val` back to a `double *` and dereference it. E.g. change `printf("%d %f", t.id, t.val);` to `printf("%d %f", t.id, *(double *)t.val);`. – Ian Abbott Feb 11 '19 at 17:30

3 Answers3

2

Several points:

  1. You do not need the intermediary pd to assign the address of d to t.val. The following should work perfectly well:
    t.val = &d;
    C allows direct assignment between void * and other pointer types. C++ does not, so this won't work in C++ without a cast:
    t.val = (void *) &d;
  2. However, this assumes that you will use t.val during the lifetime of d. If there is a chance that d will go out of scope before you use t.val, then you should allocate a new block of memory to store the value of d:
    t.val = malloc( sizeof d );
    if ( t.val )
      *(double *)t.val = d;
    
    So, instead of storing the address of d in t.val, you're storing the address of a dynamically-allocated block of memory, and copying the value of d to that dynamic block. Since t.val is a pointer to void, you first need to use the cast expression (double *) to tell the compiler to treat it as a pointer to double. Then you need to dereference that pointer with the unary * operator to assign the value of d to that new memory block. This also means you will need to deallocate that block when you're done with it:
    free( t.val );
  3. Either way, you print the value that t.val points to as follows:
    printf( "%d %f\n", t.id, *(double *)t.val );
    Again, we have to tell the compiler to treat t.val as a pointer to double and use the unary * operator to obtain the value that the pointer points to.

EDIT

Here's a very contrived example of what I mean about d "going out of scope". Let's assume that we create the t object in main, then call one function that assigns t.val using a local variable in that function, and then call another function to print the value that t.val points to:

/**
 * Sets the val member of a struct Table object. Since we want to 
 * modify the contents of the parameter, we need to pass a pointer
 * to it.
 */
void assignVal( struct Table *tbl ) 
{
  double d = some_value();        // d only exists for the duration of this function
  ...
  tbl->val = malloc( sizeof d );  // since tbl is a pointer, use ->
  if ( tbl->val )                 // instead of . to access members
    *(double *) tbl->val = d;
  else
    // error could not allocate memory for tbl->val
  ...
}

void printVal( struct Table tbl )
{
  ...
  if ( tbl.val )
    printf( "%d %f\n", tbl->id, *(double *) tbl.val );
  else
    // tbl.val was never allocated
  ...
}

int main( void )
{
  struct Table t;
  assignVal( &t );
  printVal( t );
  if ( t.val )
    free( t.val );
}

Since we want assignVal to modify the contents of t, we need to pass a pointer to it as the argument. The syntax tbl->val is the same as (*tbl).val - we dereference tbl before attempting to access the val member.

Since d ceases to exist as soon as assignVal exits, we need to allocate a new object to store d's value. Once we are finished with that value, we free t.val.

In this example, assignVal and printVal both assume that t.val points to a double object. For more general-purpose code, you may want to add another member that keeps track of the type of object val points to.

For example:

struct Table {
  int id;
  int valueType;
  void *val;
};

void assignDblVal( struct Table *tbl )
{
  double d = ...;
  ...
  t->valueType = DOUBLE; // where DOUBLE is some kind of integer constant
  t->val = malloc( sizeof d );
  if ( t->val )
    *(double *) t->val = d;
  ...
}

void assignIntVal( struct Table *tbl )
{
  int x = ...;
  ...
  tbl->valueType = INT;
  tbl->val = malloc( sizeof x );
  if ( tbl->val )
    *(int *) tbl->val = x;
  ...
}

void printVal( struct Table tbl )
{
  switch( tbl.valueType )
  {
    case CHAR:
      printf( "%d '%c'\n", tbl.id, *(char *) tbl.val );
      break;

    case INT:
      printf( "%d %d\n", tbl.id, *(int *) tbl.val );
      break;

    ...

    case DOUBLE:
      printf( "%d %f\n", tbl.id, *(double *) tbl.val );
      break;

    ...
  }
}

This way, printVal doesn't have to assume that tbl.val points to a double - we tell it what type it points to, and it uses the right cast and format specifier to do the printing.

Bear in mind, these are quick-n-dirty examples. There are much cleverer ways of dealing with type polymorphism in C. I just can't think of one right now.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Thank you very much, it would be great if you could show an example of how to use it in another function "out of scope" like stated in paragraph 2. If i will use it out of cope of d can i also use a void pointer the same way (to store a pointer to any type)? –  Feb 11 '19 at 21:10
  • I dont know if i understand it well. The first way, when i create an item to struct in the main function i cant use it in `testfunc()` for e.g? If so then i dont understand the difference in the example. –  Feb 11 '19 at 23:06
1

To print the value try

printf("%d %f", t.id, *(double*)t.val);

t.val is a pointer, not the value itself, but as it is a void* you cannot dereference it without a cast to the correct type, which is (double*) in your simple example.

To store the data you don't need the three pointers. You could simply assign the address:

t.id = i;
t.val = &d;

There are two possible problems with this approach.

  • If you want "to store different types of data" you have to know what type is stored in your struct Table when you want to use (print) the value, as you have to use the corrent cast. (Maybe you need an additional type field in your structure.)

  • If you use a variable with automatic or static storage (as double d = 2.5; in the example) and store its address in t.val you have to make sure that the variable doesn't get invalid and that you don't modify its value later. (Maybe you need dynamic memory allocation to create a copy of the variable.)

Bodo
  • 9,287
  • 1
  • 13
  • 29
0

The size of a double is 64 bits, while a void pointer is 32 bits on a 32-bit program and 64 bits in a 64-bit program.

So you can do that in a 64 bit executable but not in a 32-bit one.

If you want to store different kinds of data in a structure like that, you could use an union that contains each of your data types. See https://www.tutorialspoint.com/cprogramming/c_unions.htm.

Sami Sallinen
  • 3,203
  • 12
  • 16