-1

C++ allows you to have a member with an incomplete data type (such as char[]) at the end of a struct (EDIT: It doesn't, see answer). I created this example but can't quite wrap my head around about why and how this works.

struct foo
{
    char padding;
    char bar[];
};

int main()
{
    char str[6] = { 'h', 'e', 'l', 'l', 'o', '\0' };

    foo* fooPtr = (foo*)str;

    std::cout << fooPtr->bar;
}

Output: ello

I would expect fooPtr->bar to either be 'e' or throw some kind of read access violation error. Why does it perfectly print the string up to the null termination character?

1 Answers1

0

C++ allows you to have a member with an incomplete data type (such as char[]) at the end of a struct.

No. C++ does not allow flexible array members. However, C does and some C++ compilers support it as a language extension.

According to the standard, your program is ill-formed.

I would expect fooPtr->bar to either be 'e'

There is no reason to expect that. bar is an array, and so it will decay into a pointer when used in a value context. When a character pointer is inserted into a stream, the operator will read the characters until null character is reached. Since there is no null character after e, the output will continue past it.

or throw some kind of read access violation error.

Access violations are undefined behaviour. You can hope for, but not expect an error.

Why does it perfectly print the string up to the null termination character?

This is how the flexible arrays work in C. You allocate a memory buffer for the object; The initial segment is used for other members, and the rest are part of the flexible array member.

Note that the example program is not correct even if we assume support for flexible array members due to aliasing violation. This would be correct, if you were to insist on using the extension:

auto& str = "hello";
auto fooPtr = (foo*)std::malloc(sizeof str);
std::memcpy(fooPtr, str, sizeof str);
std::cout << fooPtr->bar;
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • So when I put fooPtr->bar in a stream like std::cout or std::string(fooPtr->bar) it will never throw an error as long as it is null terminated? – Honest Abe Apr 05 '17 at 09:18
  • @HonestAbe I don't see any reason why it should - unless there is an unrelated memory corruption. – eerorika Apr 05 '17 at 09:19
  • Reasoning about the behaviour of code after UB is pointless. Unless you can identify a particular compiler extension and include it as a reference. – Richard Critten Apr 05 '17 at 10:45