1

I'm getting this error:

list.c list.h types.h
list.c: In function 'List_push':
list.c:11:23: error: invalid initializer
--- void *values[len] = ls->values);

EDIT:

Now, with my current code (I've done undos/redos that removed somethings), I get this error instead:

gcc

Why?

Basically I've a List structure which declares an variable-length array, containing void pointers (what I want is pointers to any data type). You can see it below at the list.h file.

I've tried a mix of changes in list.c (i.e., *values[pos++] = ..., etc...), but doing these changes it only results in worse gcc errors.

wscom.c

#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "types.h"

int main() {
    List ls;
    // TEST: Put a value pointer at index 0
    uint8 value = 0x41;
    List_push(&ls, 1, &value);
    printf("%c",
        *(char*) List_getindex(&ls, 0)
    );
    return 0;
}

types.h

#ifndef hydroTrackerTypesH
#define hydroTrackerTypesH

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long long uint32;

#endif

list.h (Declarations)

#ifndef hydroTrackerListH
#define hydroTrackerListH

#include "types.h"

typedef struct {
    uint32 length;
    void *values[];
} List;

void  List_push(List *ls, uint8 count, ...);
void *List_getindex(List *ls, uint32 i);
void  List_setindex(List *ls, uint32 i, void *v);

#endif

list.c (Defns.)

#include "list.h"
#include "types.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>

static size_t PointerSize =
    sizeof(void*);

void List_push(List *ls, uint8 count, ...) {
    uint32 len = ls->length;
    void *values[len] = ls->values;
    uint32 sum = len + count;
    realloc(&values, sum * PointerSize);
    ls->length = sum;

    va_list newVals;
    va_start(newVals, count);
    uint8 pos = len;

    while(count--)
        values[pos++] = va_arg(newVals, void*);

    va_end(newVals);
}

void *List_getindex(List *ls, uint32 i) {
    return (void *)(ls->values[i]);
}

//void List_setindex(List *ls, uint32 i, void *v);
14
  • 2
    void *values[0] declares an array of 0 elements. What would you think that the compiler should do with this? Commented May 7, 2017 at 14:55
  • 1
    If I understand your code right you have to use void **values; instead of void *values[0]. void **values; has to be read as a pointer to a void pointer (or multiple of them). This is meant for the struct List as well as in the function List_push(). You may use values like an array of void* (e.g. with subscript operator []). This is a C specialty unknown in other languages like Javascript or Pascal... Commented May 7, 2017 at 15:17
  • 1
    List#length is not C. Try to express what you want in C. "I'll use it safely". Irrelevant. Array size must be known at array declaration time, and it must be a compile-time constant in most cases (including yours). None of the above holds, so the code is invalid, end of story. "but I still got compilation errors" This doesn't give you a license to use values[len]. You need to use void ** values, if you have problems using it, ask a question. Commented May 7, 2017 at 15:43
  • 1
    void *values[0]; declares a zero length array, which is not legal in C. void *values[]; declares a flexible member which your compiler doesn't support and which you don't want to use anyway. "I don't know how to use the variable of void ** values approach" That's a problem you need to solve. It looks like you know about realloc. If you have written a working program with realloc once, it contains everything you need to find your way through this problem. If not, perhaps you should step back and do some more basic exercises. Commented May 7, 2017 at 16:04
  • 1
    Don't use the flexible member feature. You don't want to, it's more complicated that you are prepared to tackle. Commented May 7, 2017 at 17:31

1 Answer 1

1

This is a little bit long for a comment. Thus, I make it an answer.

I try to show you how pointers and arrays are related to each other:

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

int main()
{
  /* The compiler allocates space for "Hello" and '\0' (5 + 1 chars)
   * and stores the address in aString1.
   */
  const char *aString1 = "Hello";
  /* The compiler allocates 10 chars and initializes
   * it with "World" (and the '\0' for terminator).
   */
  const char aString2[10] = "World";
  /* The compiler determines length of initializer "I'm here."
   * (9 + 1) and allocates the array of appropriate size.
   */
  const char aString3[] = "I'm here.";
  /* allocate storage for array (3 const char*) */
#if 0 /* the usual way */
  const char **array = malloc(3 * sizeof (const char*));
#else /* how Matheus wants to do it */
  const char **array = NULL;
  array = realloc(array, 3 * sizeof (const char*));
#endif /* 0 */
  /* assign contents (using it like an array) */
  array[0] = aString1;
  array[1] = aString2;
  array[2] = aString3;
  /* apply array to another variable array2 */
  const char **array2 = array; /* assigns the address only */
  /* use it: */
  printf("array2[0]: '%s', array2[1]: '%s', array2[2]: '%s'\n",
    array2[0], array2[1], array2[2]);
  /* throw away storage of array (and array2) */
  free(array);
  /* Attention! array, array2 become wild pointers at this point
   * and may not be accessed (except new, valid addresses are assigned).
   * However, aString1, aString2, aString3 are still intact.
   */
  printf("aString1: '%s', aString2: '%s', aString3: '%s'\n",
    aString1, aString2, aString3);
  /* done */
  return 0;
}

The sample can be tested on ideone.com.

The sample output is:

array2[0]: 'Hello', array2[1]: 'World', array2[2]: 'I'm here.'
aString1: 'Hello', aString2: 'World', aString3: 'I'm here.'

Update:

So, I finally looked again on to the question & answer of Matheus and tried to fix it according to his intention (or how I understood it). I based it on Matheus' implementation and remarked modified codes by comments:

list.h:

#ifndef LIST_H
#define LIST_H

#if 0 /* not necessary to define these types */
#include "types.h"
#else /* they are already available in a (better) portable manner: */
#include <stdint.h>
/* Btw. I had to change:
 * uint8 -> uint8_t
 * uint32 -> uint32_t
 */
#endif /* 0 */

typedef struct {
  uint32_t length;
#if 0 /* gcc ERROR: */
  /* list.c:17:3: error: invalid use of flexible array member
   * ls->values = NULL;
   */
  void *values[];
#else /* (not) 0 */
  void **values;
#endif /* 0 */
} List;

void  List_init(List *ls);
void  List_push(List *ls, uint8_t count, ...);
void* List_getindex(List *ls, uint32_t i);
void  List_setindex(List *ls, uint32_t i, void *v);

#endif /* LIST_H */

list.c:

#include "list.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

#if 0 /* no need for a variable (with storage */
static size_t PointerSize = sizeof(void*);
#else /* use enum instead (constant) */
enum { PointerSize = sizeof(void*) };
#endif /* 0 */

void List_init(List *ls)
{
  ls->length = 0;
  /* This is important: */
  ls->values = NULL;
  /* or 1st realloc() in List_push() may have Undefined Behavior.) */
}

void List_push(List *ls, uint8_t count, ...)
{
  uint32_t len = ls->length;
  uint32_t sum = len + count;
  void **values = realloc(ls->values, sum * PointerSize);
  if (!values) {
    /* realloc() failed! Bail out before destroying the existing data. */
    return;
  }
  ls->length = sum;
  ls->values = values;
  /* assign new contents */
  va_list newVals;
  va_start(newVals, count);
#if 1 /* the readable way: */
  int pos = len;
  while (count--) values[pos++] = va_arg(newVals, void*);
#else /* the hackish C style way: */
  values += len;
  while (count--) *values++ = va_arg(newVals, void*);
#endif /* 1 */
  va_end(newVals);
}

void* List_getindex(List *ls, uint32_t i)
{
  return ls->values[i];
}

wscom.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "list.h"

int main()
{
  List ls;
  /* Put a value pointers at indices 0, 1, 2 */
  uint8_t value1 = 0x41, value2 = 0x42;
  uint8_t value3[3] = { 0x43, 0x44, 0x45 };
  List_init(&ls);
  List_push(&ls, 3, &value1, &value2, value3);
  /* Check whether list contents can be retrieved again */
  if ((*(uint8_t*)List_getindex(&ls, 0)) == 0x41) {
    printf("List entry 0 is correct.\n");
  }
  if ((*(uint8_t*)List_getindex(&ls, 1)) == 0x42) {
    printf("List entry 1 is correct.\n");
  }
  { uint8_t *values = List_getindex(&ls, 2);
    if (values[0] == 0x43
      && values[1] == 0x44
      && values[2] == 0x45) {
      printf("List entry 2 is correct.\n");
    }
  }
  /* Done. */
  return 0;
}

In one of my comments, I stated that void *values[]; in struct List might be OK. Ahem, I was wrong. gcc remarks this as error when I tried to use it in list.c. So, actually, it is OK but not for what I intend it to use.

Finally, my sample session (using gcc in cygwin on Windows 10):

$ gcc -std=c11 -o wscom wscom.c list.c

$ ./wscom
List entry 0 is correct.
List entry 1 is correct.
List entry 2 is correct.

$

2nd Update:

(I believe) I realized the missing piece of Matheus (considering his Javascript background):

There are no dynamic arrays in C (in opposition to Javascript). Instead, there are arrays with variable size which may be used only in specific situations:

In C:

  1. Definition of arrays with variable size in global variables is prohibited. (The compiler needs to know how many bytes to allocate for storage.) This does not exclude something like e.g.
    int array[] = { 1, 2, 3 };
    because the compiler determines the size from the initializer (on the right hand side of =).

  2. Declaration of global arrays without explicit size is possible. (The definition with proper size might/must be done somewhere else. The linker will fail if no proper storage definition can be found.)

  3. A local variable (inside a function, storage class auto but not static or extern) might be declared as array with size determined at runtime (from a variable). This feature was introduced in C99 but not (yet) in C++ (at least not until C++11 incl.)

  4. A function parameter might be declared as array with unknown (or any) size. (This is equal to declaring it as a pointer.)

I found a nice answer about this in SO: Dynamic array allocation on stack in C (which I used to prove my own statements above).

The only supported way to have "dynamic arrays" in C is the usage of the standard library functions malloc()/realloc()/free(). However this is better called "dynamic memory" allocation because this applies to any C type (not only arrays).

Disclaimer:

I apologize if I wrote something rubbish about Javascript. I'm the total newbie in Javascript with very less practical experience...

Sign up to request clarification or add additional context in comments.

7 Comments

I'm lunching too, I'll give it a check soon soon
It worked in my compiler. So weird that I couldn't do void *values[] before, now it also compiles this way, but doesn't print what I want yet...
@Matheus I improved the sample and added a link for demonstration.
@Matheus I updated my answer and added a fixed version according to your sample code.
@Matheus Did you notice the missing address operator (&) before value3 in the call of List_push() in main()? This is intentionally. An array var. provides actually its base address (address of 1st element). The subscript operator ([]) is actually a binary operator which takes base address and index and returns reference to the addressed element. (That's why the latter may be applied to pointers also.)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.