1

How to copy to flexible array inside struct in c?

    #include <stdio.h>
    #include <string.h>
    
    typedef struct
    {
        int val;
        char buf[];
    } foo;
    
    int main()
    {
        foo f;
        f.val = 4;
        // f.buf = "asd"; -> invalid use of flexible array member
        memcpy(f.buf, "asd\0", 4);
        printf("%s\n", f.buf);
    }

output:

    asd
    *** stack smashing detected ***: terminated
    Aborted (core dumped)

Also, if the struct was declared as:

    typedef struct
    {
        char buf[];
    } foo

vscode editor gives error:

    incomplete type is not allow

and gcc gives error:

    error: flexible array member in a struct with no named members
        6 |     char buf[];

Why is array in struct now allowed but pointer is? (char *buf).

Also, If a struct has a flexible array, what is its sizeof(struct containsFlexArray)? How can I dynamically resolve its array, when it has no dimension?

EDIT: if the above works in C++, because the incomplete array "decay" to pointer of known length (8 bytes in x64), why is this not also the case in c? If I peek to asm, I see the program does not allocate enough stack for the struct (it allocates only space for foo.val member, but not bur foo.buf member, in which case the program tries to use override the foo.val member (by using its address instead of foo.buf), which causes the stack smashing detected. But why is it implemented this wrong way? (So I want to know the rationale behind introducing flexible array as well)

Pat. ANDRIA
  • 2,330
  • 1
  • 13
  • 27
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • May be I do not understand what your problem is but I have no problem with both of your examples. However I reproduce the second error when compiling in `C` but it compiles and executes succesfully in `C++` – Pat. ANDRIA Jan 30 '21 at 17:05
  • If you have structure with just one member - incomplete array (e.g. `int ar[]` or `char ar[]`), will your c++ compile? If so then there is difference between `c` and `c++` becuase in `c` it wouldn't compile with error of `flexible array member in a struct with no named members` (probably of missing dimension in array no defining its length in the brackets, but I would suspect in c++ the array decay to pointer of known length 8 bytes) – milanHrabos Jan 30 '21 at 17:10
  • 2
    *if the above works in C++* Don't worry - it ***doesn't*** work in C or C++. You're invoking undefined behavior by attempting to copy information to `f.buf` when it hasn't had any memory allocated to it. Just because you don't ***observe*** a failure with undefined behavior doesn't mean it works properly. – Andrew Henle Jan 30 '21 at 17:20
  • "incomplete type is not allow" search for that error message (fix the spelling though) and read some of the things you find to get an idea what it means. – Ulrich Eckhardt Jan 30 '21 at 17:32
  • @AndrewHenle is right, but it works in C++ because MS VisualStudio compiler does not use standard but it rather has its own rules. –  Jan 30 '21 at 18:48

3 Answers3

3

You may want to read information on flexible array member here.

It seems as, when using a flexible array in a struct there must be at least one other data member and the flexible array member must be last.

And also you may have an element of clarification concerning the usage of flexible array members in C here

Pat. ANDRIA
  • 2,330
  • 1
  • 13
  • 27
1

lets use intel/amd architecture here where char => 1 byte int => 4 and long is 8 bytes long.

Struct alignment is an issue here. You see when you declare a struct in c, compiler looks at it as individual block. So if you have a struct like this:

struct a {
   long l;
   char c1;
   char c2;
}

compiler looks at the first type used, and allocates 8 bytes of memory for l, looks at c and determines that c1 is shorter than l and rather than figure out what c1's type is, it allocates 8 bytes of data for c1. It does the same for c2. So you end up with struct that is 24 bytes long and only 10 are used. Where if you use this:

struct b {
  char c1;
  long l;
  char c2;
}

this will allocate 1 byte for c1, 8 byte for l, and 8 bytes for c2. So you end up with 17 bytes and 10 used. Where as if you have this:

struct b {
  char c1;
  char c2;
  long l;
}

well it allocates 1 byte for c1, 1 byte for c2, and 8 bytes for l. In total 10 bytes but all 10 are used.

So what does it have to do with array? You see if you have:

struct aa {
   char a;
   long b[];
}

This will know to allocate at least one byte for b initially. Where when you do not have char a,

struct aa {
   long b[];
}

Compiler might not allocate any memory (allocate 0 bytes), because it simply does not know how much to allocate.

EDIT: Left my PC and in mean time other answer popped up. The other answer is very good!!! But I hope this helps you understand what is going on.

1

You did not initialize the buf[] array when you declared an instance in main(). The compiler does not know how much memory to allocate. The stack smashing is a compiler feature that keeps your program from doing... bad things to you computer. Add a number to your array declaration in typedef struct. Like this: ` #include <stdio.h> #include <string.h>

typedef struct
{
    int val;
    char buf[5];
} foo;

int main()
{
    foo f;
    f.val = 4;
    // f.buf = "asd"; -> invalid use of flexible array member
    memcpy(f.buf, "asd\0", 4);
    printf("%s\n", f.buf);
}`