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);
  • 2
    `void *values[0]` declares an array of `0` elements. What would you think that the compiler should do with this? – Jens Gustedt May 07 '17 at 14:55
  • @JensGustedt Yes, I've a made an undo/redo mistaken in my code. It was supposed to be `void *values[len]`, for example. –  May 07 '17 at 15:06
  • 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... – Scheff's Cat May 07 '17 at 15:17
  • "It was supposed to be void *values[len], for example" What is `len`? Is it known at the point of declaration of `values`? Is it a constant expression? If not, you cannot have `void *values[len]`, you need `void** values`. In other news, (1) your `List_push` interface is dangerous, requiring the second argument and the number of pointers to be in sync; and (2) `List` isn't going to initialize itself, you need to explicitly set up an empty list, and you want a dedicated function to do just that. – n. m. could be an AI May 07 '17 at 15:22
  • @Scheff So that's like declaring nested pointers, right? Unfortunately I got compilation errors in different ways of using this pointer :/ –  May 07 '17 at 15:34
  • @n.m. I've updated the question. This `len` is just `List#length`, which I won't change in other parts of the code (I'll use it safely). I've tried this special `**` pointer way as suggested by Scheff, but I still got compilation errors on its usage. I thought `List` would be `0` at all. I thought on creating an initializer function too before (but I'm not sure this affects the compilation errors). –  May 07 '17 at 15:34
  • 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. – n. m. could be an AI May 07 '17 at 15:43
  • @n.m. I got it, then `typedef struct { void *values[0]; } List;` is not what I want, right? There's no way because `typedef struct { void *values[]; } List;` causes a syntax error. Anyways, out of this, I don't know how to use the variable of `void ** values` approach :v. It compiles without errs, but when I do `if ((*(uint8*)List_getindex(&ls, 0)) == 0x41) printf("Wow");` in main code, it doesn't print (`List_getindex(&ls, 0)` should return the value from `uint8 value = 0x41;`). –  May 07 '17 at 15:54
  • 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. – n. m. could be an AI May 07 '17 at 16:04
  • @n.m. It looks like my compiler does support this flexible member feature, but it somehow didn't let me use it before. Thanks for the motivation –  May 07 '17 at 17:01
  • 1
    Don't use the flexible member feature. You don't want to, it's more complicated that you are prepared to tackle. – n. m. could be an AI May 07 '17 at 17:31
  • Use `#include ` instead of your `types.h`, it will be much less confusing for everybody reading your code – M.M May 07 '17 at 22:31
  • @M.M I remind I've used some of these `uint8_t` and such types, thanks for the suggestion. –  May 07 '17 at 22:39
  • [you should copy the error output text and paste here instead of capturing and post as an image](https://meta.stackoverflow.com/q/303812/995714) – phuclv May 08 '17 at 07:44

1 Answers1

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

Community
  • 1
  • 1
Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • I'm lunching too, I'll give it a check soon soon –  May 07 '17 at 16:28
  • 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... –  May 07 '17 at 16:46
  • @Matheus I improved the sample and added a link for demonstration. – Scheff's Cat May 08 '17 at 06:44
  • @Matheus I updated my answer and added a fixed version according to your sample code. – Scheff's Cat May 08 '17 at 07:21
  • @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.) – Scheff's Cat May 08 '17 at 07:42
  • @Matheus May be, I realized your "missing piece". I added a 2nd update. – Scheff's Cat May 08 '17 at 08:49
  • Very thanks for the complete answer! I still find somethings about `**` pointer confusing, I didn't think it could be allocated... so any memory being pointed at can be accessed as an array in C, right? But does it need to be a nested-pointer as `**`, really? Anyways it is working as I much expected –  May 08 '17 at 15:35