14

Can we declare a structure element of variable length?

The condition is as follows:

typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t Employee_Names[No_Of_Employees][15];
}st_employees;
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
user12345
  • 357
  • 2
  • 3
  • 11
  • 2
    What does your compiler say? – Arkadiusz Drabczyk Aug 31 '15 at 12:32
  • 1
    What you can do instead, is to declare the array as a _flexible array member_, which you can later allocate room for in runtime. Example from the C standard: `struct s { int n; double d[]; }; int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));` – Lundin Aug 31 '15 at 12:42
  • I am using IAR gcc compiler – user12345 Aug 31 '15 at 13:11
  • What version of the [GCC](http://gcc.gnu.org/) compiler? What compilation command line? Did you compile with `gcc -std=c99 -Wall` ? – Basile Starynkevitch Aug 31 '15 at 13:15
  • @user12345 Unless I've been living underneath a rock, IAR and GCC are distinctively different compilers. Out of curiousity, which one is the target processor? – Lundin Aug 31 '15 at 14:14
  • I implemented suggestion given by daouzli. Since I am working on embedded development, I cannot use malloc functions because of stack sizes. Anyways, thank you all for getting me the logic to use in future :) – user12345 Sep 08 '15 at 09:47

5 Answers5

25

If coding in C99 or C11, you might want to use flexible array members (you don't give an explicit dimension, but you should have a convention about it at runtime in your head).

 typedef struct {
    unsigned No_Of_Employees;
    char* Employee_Names[]; // conventionally with No_of_Employees slots
 }st_employees;

As for any array, each slot of a flexible array member has a fixed size. I'm using a pointer (e.g. 8 bytes on my Linux/x86-64 machine).

(In old compilers before the C99 standards, you might try give a 0 dimension like char* Employee_Names[0]; even if it is against the standard)

Then you would allocate such a structure using e.g.

 st_employees* make_employees(unsigned n) {
    st_employees* s = malloc(sizeof(s_employees)+n*sizeof(char*));
    if (!s) { perror("malloc make_employees"); exit(EXIT_FAILURE); };
    s->No_of_Employees = n;
    for (unsigned i=0; i<n; i++) s->Employe_Names[i] = NULL;
    return s;
 }

and you might use (with strdup(3) duplicating a string in the heap) it like

 st_employees* p = make_employees(3);
 p->Employee_Names[0] = strdup("John");
 p->Employee_Names[1] = strdup("Elizabeth");
 p->Employee_Names[2] = strdup("Brian Kernighan");

You'll need a void destroy_employee(st_employee*e) function (left as an exercise to the reader). It probably should loop on i to free every e->Employee_Names[i], then free(e);...

Don't forget to document the conventions about memory usage (who is in charge of calling malloc and free). Read more about C dynamic memory allocation (and be scared of memory fragmentation and buffer overflows and any other undefined behavior).

If using a GCC older than GCC 5 be sure to compile with gcc -std=c99 -Wall since the default standard for old GCC 4 compilers is C89. For newer compilers, ask for all warnings and more of them, e.g. gcc -Wall -Wextra...

Jo So
  • 25,005
  • 6
  • 42
  • 59
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    I am an embedded programmer. I am using IAR compiler and it is giving error as : expression must have a constant value. I have enables C99 standard and also VLA. – user12345 Aug 31 '15 at 13:09
  • 3
    Then you should upgrade your compiler to a C99 compliant one. Try [GCC](http://gcc.gnu.org/) – Basile Starynkevitch Aug 31 '15 at 13:09
  • I am using C99 standard and enabled VLA – user12345 Aug 31 '15 at 13:10
  • Then you should consider upgrading your [GCC](http://gcc.gnu.org/) perhaps by downloading its source code (for [GCC 5](https://gcc.gnu.org/gcc-5/)) and building the compiler from source. – Basile Starynkevitch Aug 31 '15 at 13:13
  • @user12345 You can try the solution I proposed. It should work. – daouzli Aug 31 '15 at 13:13
  • Yes I am checking on that only. Thank you every one for your feedbacks :) – user12345 Aug 31 '15 at 13:44
  • Alternatively, you could declare the inner-most dimension empty (flexible array) and the outer-most dimension as a fixed 15. – Lundin Aug 31 '15 at 14:08
  • 1
    @user12345 It may be that IAR doesn't support strdup, which is a non-standard function. You can replace strdup with a malloc + strcpy. – Lundin Aug 31 '15 at 14:11
  • Great theme. I am using MSVS and it works as long as the variable array is last (is logical). If one need multiple variable arrays in a struct it can be made by sub-structs with just a variable a array and a pointer to it, I guess? To make it expandable the sub-structs could be realloced, or do I have to? And that must be the way they made it to support the topic in like Java? Also these variable arrays can be 0-terminalted instead of needing to keep count permanently (counted by use). Nice to hear a smart experienced comment on this? And also how to make the code look stylish (proper). – Jan Bergström May 30 '22 at 18:02
10

TL;DR answer - No, you cannot.

To elaborate, let me quote C11, chapter §6.7.2.1, Structure and union specifiers (emphasis mine)

A member of a structure or union may have any complete object type other than a variably modified type. [...]

and, a VLA is a variably modified type.

However, quoting from the same standard, regarding the flexible array member

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. [...]

So, you can do something like

typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t* Employee_Names[];
}st_employees;

and later, you can allocate memory dynamically at the runtime to Employee_Names (and Employee_Names[i], too) and make use of it.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
2

NO,

When you define a structure, its size has to be confirmed, so that when you declare a variable of that structure type, memory can be allocated for that variable.

Think about this scenario. When you want to declare a variable p of the type st_employees, since the No_Of_Employees is not set yet, the size of the variable p is not confirmed, hence memory for the variable cannot be allocated. But you cannot set No_Of_Employees without declaring a variable of type st_employees. Its a paradox.

Haris
  • 12,120
  • 6
  • 43
  • 70
  • Don't flexible array members contradict the purported requirement that a structure's size must be confirmed at definition? – Kröw Jan 24 '23 at 09:52
1

To my understanding, this is not possible; it is impossible to have one field of a struct defined in terms of a different field.

Codor
  • 17,447
  • 9
  • 29
  • 56
0

You can do this with dynamic allocation as follow:

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

#define SIZE_OF_ELEM 15
#define uint8_t char 
typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t **Employee_Names;
}st_employees;

int main()
{
    int i;
    st_employees emps;
    emps.No_Of_Employees = 2; //number of elements
    // allocate the number of elements
    emps.Employee_Names = malloc(emps.No_Of_Employees);
    for (i=0; i < emps.No_Of_Employees; i++)
    {
        // allocate each element
        emps.Employee_Names[i] = malloc(SIZE_OF_ELEM);
        // fill the element with some data
        sprintf(emps.Employee_Names[i], "emp_n%d", i);
    }
    // show the content
    for (i=0; i<emps.No_Of_Employees; i++)
    {
        printf("Employee %d content: %s\n", i, emps.Employee_Names[i]);
    }
    return 0;
}

Of course this is an illustration, you have to check allocation, precise the sizeof type and release memory.

Note that this method allows to create a collection of objects that can have different types, and there is no need to use any particular C compiler version or options.

However, in very basic case (as in the OP example) it is not the better solution as it will fragment the memory (one allocation per object). So use this with caution.

daouzli
  • 15,288
  • 1
  • 18
  • 17
  • 5
    No don't do this, there's absolutely no reason to fragment the struct's memory all over the heap. Instead declare `Employee_Names` as a flexible array member and allocate room for the struct + the size of the array at the same time, in adjacent memory. Example from the C standard: `struct s { int n; double d[]; }; int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));` – Lundin Aug 31 '15 at 12:44
  • I disagree. This is a way to do in a flexible way. It allows you to use any type of data. In that particular exemple it is not the better way but the question is to use variable struct length data and the exemple shows an element of a particular size. – daouzli Aug 31 '15 at 12:50
  • @Lundin unfortunately you downvote a working solution for the problem while there is no other solution proposed. – daouzli Aug 31 '15 at 13:02
  • 1
    My solution should work. I guess that the OP forgot the `-std=c99` flag to [GCC](http://gcc.gnu.org/); and your solution is indeed fragmenting the memory a lot, which is an issue in embedded software. – Basile Starynkevitch Aug 31 '15 at 13:18
  • 2
    @daouzli I downvoted because you preach bad practice: unnecessary fragmentation of the heap (fake arrays), instead of allocating everything in adjacent memory (true arrays). In addition to heap fragmentation, your method also causes your program to become needlessly slow, for nothing gained. Not only because of various table lookup-overhead instructions between the lines, but perhaps more importantly because adjacent memory cells mean that your program can utilize on-chip data cache memory far better. – Lundin Aug 31 '15 at 13:59
  • I think solution from daouzli should work . I am working on it. I will check and come back. Thank you everyone. – user12345 Aug 31 '15 at 14:00
  • 2
    @user12345 It works but it is suboptimal and bad practice. To make a silly real life example: you could decide to store your cookies in the cookie jar, or you can put one cookie in each room of the house. Both methods stores cookies. The latter method involves a whole lot of running around all over the place searching, each time you want a cookie. Instead of just picking one up from the expected location in the cookie jar, where all the other cookies are. – Lundin Aug 31 '15 at 14:04
  • @Lundin, as I said, I hear your argument and you're right in case you have a collection of the same type. But my method works even in case where you cannot know the types you store before runtime. – daouzli Aug 31 '15 at 14:41
  • @user12345 what about your problem ? did you succeed ? what solution was adapted ? – daouzli Sep 01 '15 at 15:28
  • @daouzli I used malloc functions as suggested by you. I was searching for the solution like the same using pointers. Thank you all :) – user12345 Sep 03 '15 at 05:50