2

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?

4
  • What's wrong with struct test *myText = malloc(size * sizeof (*myText)) ? Commented Apr 3, 2019 at 19:15
  • Or more to the point, what is the actual problem you're trying to solve? Commented Apr 3, 2019 at 19:18
  • @Broman That's okay. But how about this: struct test *myStruct = myText + 1; Commented Apr 3, 2019 at 19:18
  • @Broman reading from inotify file descriptor files supplied buffer with struct inotify_event which contains flexible array member. Commented Apr 3, 2019 at 19:36

2 Answers 2

3

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.

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

3 Comments

So sinnce malloc allocates for either an object or array of the objects there is no pedantic way. Correct?
@SomeName It's not about malloc so much as it's about pointer arithmetic. Because each struct can have a different size, the array can't be indexed normally.
agree, since pointer arithmetic is well defined only on elements of the same array, thanks.
1

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.

3 Comments

I would argue about disabling strict aliasing to access a common initiail sequence of non-compatible struct types. Putting both of the types as union members and accessing their common initial sequence from anywhere where the union declaration is visible is a conforming way, isn't?
@SomeName: Under the gcc/clang interpretation, operations that take the addresses of union members and use the resulting pointers are unsupported *even in cases where nothing else accesses the storage between the time the address is taken and the pointer is used. Even something like *(someUnion.memberOfArrayType+index) can fail. The Standard doesn't forbid such behavior because the authors expected that compiler writers would limit application of the rule to cases involving aliasing of seemingly-unrelated objects. More generally, the Standard deliberately allows...
...implementations specialized for narrow purposes to behave in ways that would benefit those purposes, even when that would make them unsuitable for many others. As a consequence of that, it makes no attempt to pass judgment as to what behaviors might render an implementation unsuitable for almost any purpose.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.