0

Say I want to dynamically allocate an array that can hold any data, i.e. using void** as my type, in C. Then, rather than re-writing the pointer arithmetic logic each time, I want a simple function that returns the address of an element. Why is it that I cannot use this function in an assignment (i.e., so I can set, as well as get the element's value)?

Here's some example code:

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

void printIntArray(void** array, size_t length) {
  printf("Array at %p\n", array);
  while (length--) {
    printf("  [%zu] at %p -> %p", length, array + length, *(array + length));
    if (*(array + length)) {
      printf(" -> %d", *(int*)*(array + length));
    }
    printf("\n");
  }
}

void* getElement(void** array, size_t index) {
  return *(array + index);
}

int main(int argc, char** argv) {
  const size_t n = 5;
  size_t i;

  /* n element array */
  void** test = malloc(sizeof(void*) * n);
  i = n;
  while (i--) {
    *(test + i) = NULL;
  }

  /* Set element [1] */
  int testData = 123;
  printf("testData at %p -> %d\n", &testData, testData);
  *(test + 1) = (void*)&testData;

  printIntArray(test, n);

  /* Prints 123, as expected */
  printf("Array[1] = %d\n", *(int*)getElement(test, 1));

  /* Something new in [2] */
  /* FIXME  lvalue required as left operand of assignment */
  int testThing = 456;
  getElement(test, 2) = (void*)&testThing;
  printIntArray(test, n);

  return 0;
}

It wouldn't be the fist time this question has been asked, but the answer is usually along the lines of "you are trying to assign something to a function, rather than the return value of a function, which is not allowed". Fair enough, but can one get around this? I'm somewhat confused!!

Xophmeister
  • 8,884
  • 4
  • 44
  • 87
  • 1
    why would you wanna do that? – nightshade Jun 26 '14 at 00:11
  • @nightshade A simple example of why you might want to do that is mentioned in one of the answers to this stackoverflow [Is errno thread-safe](http://stackoverflow.com/questions/1694164/is-errno-thread-safe) which has an `errno` implementation that is thread safe due to the use of `errno` being a macro that uses a function which returns an int pointer to a particular thread's `errno` memory. – Richard Chambers Jun 26 '14 at 02:01
  • @nightshade Just as an exercise, I was writing a dynamic array library with bounds checking. The above example code was simplified just to illustrate the problem. – Xophmeister Jun 26 '14 at 22:37

4 Answers4

2

Assignment to a void is not allowed so you will need to cast the void pointer to some other type of pointer for any kind of an assignment.

In other words,

void *vPtr = malloc(35);

*((int *)vPtr) = 5;   // legal since cast void * to int *
 *vPtr = 5;           // illegal since size of the memory location pointed to is unknown.

The same principle applies to a function that returns a void pointer.

You just have to cast the pointer returned by the function to a type that allows assignment.

A simple example that compiles in Visual Studio 2005 is.

void *xPtr (int *pp)
{
    return pp;
}

void jj ()
{
    int jjj;
    *( (int *)xPtr(&jjj)) = 6;
}

Where as if you change the function jj() to

void jj ()
{
    int jjj;
    *(xPtr(&jjj)) = 6;
}

you will see compile errors such as

1>c:\fileobject.c(191): error C2100: illegal indirection
1>c:\fileobject.c(191): warning C4047: '=' : 'void *' differs in levels of indirection from 'int'
1>c:\fileobject.c(191): error C2106: '=' : left operand must be l-value
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • The OP is referring to `void **` not `void *`. Teh fromer can very well be dereferenced and then get soemthing assigened, namely another `void*`. – alk Jun 26 '14 at 06:57
2

Implement getElement() like this:

void ** getElement(void ** ppv, size_t index) {
  return ppv + index;
}

And use it like this:

 *getElement(test, 2) = &testThing;

If you'd use a macro you could even go the way you intended:

#define GET_ELEMENT(ppv, index) \
  (*(ppv + index))

Use it like this:

GET_ELEMENT(test, 2) = &testThing;
alk
  • 69,737
  • 10
  • 105
  • 255
1
getElement(test, 2) = (void*)&testThing;

This won't ever work. You cannot assign a value to a function. Use this:

test[2] = (void*)&testThing;

Note that getElement(test, 2) returns NULL, it is not a pointer to the element index 2 of your array test, it's its value (that is also a pointer, but not the one you need). No matter what you do with the information getElement(test, 2) provides you, you can never use it to change test[2].

Say its a situation where the caller don't have access to test, you would have to write a setElement() function.

void setElement(void** array, size_t index, void* value) {
  array[index] = value;
}
...
setElement(test, 2, (void*)&testThing);
Havenard
  • 27,022
  • 5
  • 36
  • 62
0

C uses pointers to fake what other languages call reference... Here is an example of what you are trying to do:

typedef struct 
{
   int x;
} st;

st f()
{
   st p;
   p.x = 20;
   return p;
}

int main()
{
    f().x = 9;
}

You are returning a value from a function... you need to assign that value to something before you use it. Yes, a pointer follows the same rule, the difference is that it's value is the address of something... so you would have to assign the return of your function to a void *variable and to what you wanna do on this variable.

In my example, to make it work you need to something like:

int main()
{
     st v = f();
     v.x = 9;
}

Same logic goes for your pointer

nightshade
  • 638
  • 5
  • 15