The compiler can't calculate how much memory struct s b[];
will consume. This means that if the structure has any fields after it, the compiler can't figure out the where those fields are.
It used to be (in old versions of C) that (e.g.) struct s b[];
wasn't allowed as a member of a structure. This made efficient memory management annoying. For a simple example, imagine you've got a structure containing a "name" string (that could be just a few characters or a lot of them). You could use a fixed size array that's big enough for the largest name (which wastes space), or use a pointer and allocate 2 pieces of memory (one for the structure and one for the variable length name string). Alternatively, you could use a pointer and make it point to extra space past the end of the structure, which ends up something like this:
length = strlen(my_string);
foo = malloc(sizeof(MYSTRUCTURE) + length + 1);
foo->name = (void *)foo + sizeof(MYSTRUCTURE); // Set pointer to extra bytes past end of structure
memcpy(foo->name, my_string, length + 1);
This was the most efficient option; but it's also ugly and error prone.
To work around that, compilers added non-standard extensions to allow "unknown size arrays" at the end of the structure. This made it a little easier for programmers and made it a little more efficient (as there's no need for the additional pointer member). This ended up being adopted by the C standard (maybe in C99 - I don't remember).