Search code examples
cpointersvoid-pointerslvalue

Using function returning pointer as lvalue


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!!


Solution

  • 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;