tags:

views:

283

answers:

3

I am a linguist in charge of a C program, so please excuse me if the answer is obvious. I have the following code:

typedef struct array_s {
  (...)
  void **value;
} array_t;
typedef array_t *array_pt;

array_pt array_new (int size) {
  (...)
  array->value = (void **)malloc(size*sizeof(void *));
}

void* array_get (array_pt arr, int i) {
  return arr->value[i];
}

int main () {
  int a = 1234;
  int *ptr = &a;

  array_pt array = array_new(1);
  array_add(array, ptr);

  printf("%i\n", (int)array_get(array, 0));
}

It is supposed to provide me with a multi-purpose array (for storing int and char*, if I understood I can only use void), and I guess there are no problems of allocating/freeing. However, I cannot get to cast it into anything useful (i.e., get back the "original" int/char*), and for what I understood it could be because I am in a 64-bit system and the size of a pointer to void is different from the size of a pointer to int/char* (the program is supposed to be used in both 64 and 32 bit systems). I tried using intptr_t and other alternatives, to no luck.

How can I be sure that the code will accept any data type and work on both 32 and 64 bit systems? Thank you.

EDIT:

Sorry for not adding array_add, here it is:

unsigned int array_add (array_pt array, void *ptr) {
  (...) // get the next index
  // allocate if needed
  array->value = (void **)realloc(array->value, array->size*sizeof(void *));

  array->value[index] = p;
}
+3  A: 

You need to dereference your pointer:

int* temp = array_get(array, 0);
printf("%i\n", *temp);

However, I strongly recommend avoiding this type of approach. You're basically giving away the small amount of help the compiler in C will normally provide - purposefully trying to make non-typesafe arrays.

Reed Copsey
Thank you for such a fast answer, Reed. However, gcc is complaining:prova.c:14: warning: dereferencing ‘void *’ pointerprova.c:14: error: invalid use of void expression
Giacomo
@Giacomo: I adjusted the syntax - it should work now.
Reed Copsey
You could do it on one line, but it's going to be really ugly ;)
Reed Copsey
It worked, thank you! Just so I can try to understand (pointers are a bit of black magic for someone who did a major in Medieval Literature), how could you do it in one line?
Giacomo
@Reed Copsey: In one line it would be ugly specifically because you'd have to use a *cast* to `int *`. You used two lines, yout you kept a cast for some reason (???), which makes it exactly as ugly as one-line approach would be. The whole point of using a temporary typed pointer is that you can do it without a cast: `int* temp = array_get(array, 0)`.
AndreyT
This should work: `printf("%i\n", *((int*)array_get(array, 0)));` - You just need to cast it to an int pointer, then dereference the int pointer.
Reed Copsey
@AndreyT: +1 Yes - I had left it in just so it was obvious why it was working - but I took it out, esp. now that I added the details in my last comment.
Reed Copsey
@Giacomo: Is this not working, now? (Just wondering why you marked it as answer, then unmarked it again...)
Reed Copsey
@Reed Copsey: I suspect that what the OP wanted is to `int` values directly in the array (cast to `void *`), not `int *` pointers. In other words, you answer "works", but it does not do what the OP intended to do.
AndreyT
@Reed Copsey: I was thinking that marking it as solved would prevent people from commenting! I marked it again, thank you very much (and yes, it is working, I am trying to understand its secrets now). :)
Giacomo
The extra parentheses are unnecessary - `*(int *)array_get(array, 0)` is fine. To the OP, be aware that the array is not storing the integer itself - it's storing the location of some integer variable (in the case of your example, the variable `a` in `main`). If that integer variable goes away (say, because the function it was declared in returns), then the array is now holding an invalid location.
caf
A: 

intmax_t should be an integer type that is 32 bits on 32bits compilers and 64bits on 64bit compilers. You could use %j in your printf statement to print intmax_t. The size of pointers on one system is always the same - independently of them pointing to int, char or void.

printf("%j\n", (intmax_t)array_get(array, 0));
krow
array_get returns a pointer - he's (I think) trying to print the value of the int being stored, not the memory address...
Reed Copsey
Well, then your answer above should help. :) It really depends on what the array_add does.
krow
I am sorry know, but it does not work: gcc sayswarning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
Giacomo
Are you sure you copied the code? It says "%j" (spell: Jay) instead of %i.
krow
+3  A: 

You need to decide what is it you are trying to do in this case.

(1) If you want to use your void * array to store int values (actual int forcefully converted to void *), then you should add these int values to the array as follows

int a = 1234; 

array_pt array = array_new(1); 
array_add(array, (void *) a); 

and then get them back from array as follows

int a = (int) array_get(array, 0); 
printf ("%d\n", a);

or simply

printf ("%d\n", (int) array_get(array, 0)));

That last part is exactly what you did, but you got the first part wrong.

This is a cast-based approach, which is ugly in many ways, but it has certain practical value, and it will work assuming void * is large enough to hold an int. This is the approach that might depend on the properties of 32- and 64-bit systems.

(2) If you want to use your void * array to store int * values (pointers to int), then you should add these int values to the array as follows

int a = 1234; 

array_pt array = array_new(1); 
array_add(array, &a); 

and then get them back from array as follows

int *pa = array_get(array, 0); 
printf ("%d\n", *pa);

or simply

printf ("%d\n", *(int *) array_get(array, 0));

This approach is perfectly safe from any portability problems. It has no 32- or 64-bit issues. A void * pointer is guaranteed to safely hold a int * pointer or any other data pointer.

If that was your intent, then you got the first part right and the last part wrong.

Either this or that. You code appears to be a strange mix of the two, which is why it doesn't work, and which is why it is impossible to figure out from your original message which approach you were trying to use.

AndreyT
It makes a lot of sense... I wanted to code to store both int values and pointers to chars (strings). Maybe I should have two different strutures?
Giacomo
You absolutely should use two different structures; this approach is error-prone, dangerous, and a nightmare to debug. Remember that any small integer value will crash the program if you try to dereference it; in fact, most random integers will.
Andrew McGregor
@Giacomo: Well, you are saying that you wanted to store `int` values, yet you accepted an answer that stores `int *` values. So, again, what is it you wanted to do?
AndreyT