简体   繁体   中英

How to allocate an array of structs with flexible array members comformingly?

I have the following struct with a flexible array member:

struct test {
    size_t sz;
    const char str[];
};

Now I want to allocate some memory to put this struct continuously (like in array). The problem is a declaration like struct test test_arr[] is undefined behavior. 6.7.2.1(p3) :

the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

We know that pointer returned by malloc can be converted to a pointer to any objecut type with fundamental alignment. Consider the following code:

void *obj= malloc(100 * sizeof(struct test)); //enough memory
struct test *t1 = obj;
t1 -> sz = 2;
t1 -> str = {'a', 'b'};
struct test *t2 = (void *) (((char *) obj) + sizeof(struct test) + sizeof(char[2])); // non conforming 

What is the conforming way of doing so?

A struct with a flexible array member can't be a member of an array, as dictated by the quote you gave.

The best way to handle this is to change the flexible array member to a pointer and allocate space for it separately.

struct test {
    size_t sz;
    char *str;
};

...

struct test *arr = malloc(100 * sizeof(struct test));

arr[0].sz = 2;
arr[0].str = malloc(2);
arr[0].str[0] = 'a';
arr[0].str[1] = 'b';

arr[1].sz = 3;
arr[1].str = malloc(3);
arr[1].str[0] = 'c';
arr[1].str[1] = 'd';
arr[1].str[2] = 'e';

Also, it's generally not a good idea to have const members of a struct.

A "popular extension" which most implementations can be configured to support (if they don't always do so) is to allow the address of structure type to be converted to the address of another which shares a Common Initial Sequence, and be used to access members of that sequence, until the structure is accessed via means other than the converted pointer, or execution enters a function or loop wherein that would occur.

On implementations that support that extension, the requested semantics can be achieved by declaring a structure whose layout which matches that of the structure with the Flexible Array Member. For example:

struct POINT { int x, y; };
struct POLYGON { int sides; struct POINT coords[]; };
struct TRIANGLE { int sides; struct POINT coords[3]; };

void draw_polygon(struct POLYGON const *p);
void test(void)
{
  struct TRIANGLE my_triangle = {3, {{1,2}, {3,4], {5,6}};
  draw_polygon((struct POLYGON*)&my_triangle);
}

Some compilers like icc and MSVC are sophisticated enough to support this extension even when type-based aliasing is enabled. Others like gcc and clang can only support this extension via the use of the -fno-strict-aliasing option.

Although code using this extension is not strictly conforming, the Standards Committee has said in the published Rationale that they did not want to make the language usable only for writing portable programs. Instead, they expected that quality implementations would support a variety of "popular extensions" by processing some constructs in ways that would be useful to their customers even when the Standard would allow them to do otherwise. The ability to convert pointers among structure types has been a fundamental part of the language the Standard was written to describe since 1974, and nearly all implementations can be configured to support it. Code like the above should thus be recognized as more portable than code which relies upon non-standard syntactical extensions to achieve similar semantics.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM