4

This code appears to break on one widely used compiler with optimizations enabled, although it works fine in Visual Studio.

struct foo
{
    foo(int a) { s[0] = '0'+a%10;s[1] = '\0'; }
    const char * bar() const { return &s[0]; }
    char s[4];
};

int main( )
{
    const char * s = foo(1234).bar();
    printf("%p %s\n", s, s);
}

I expect that even though foo is an rvalue, that it will be constructed on the stack, and 's' will point to valid data. However, with GCC (versions 7.5 and 9), the buffer is never initialized.

Example failing compile line:

g++-9 --std=c++11 -Wall -pedantic -O3 -o example example.cpp
A4April
  • 43
  • 3
  • make the s variable volatile. – bakaDev Sep 12 '20 at 01:03
  • Perhaps https://stackoverflow.com/q/584824/2864740 - I think since C++11 things may have been codified better (mainly at the end of the expression, not scope or compiler specific). Note that *s is dangling after the expression. – user2864740 Sep 12 '20 at 01:05
  • 1
    It would be expected to work without the local *s variable.. `printf("%s\n", foo(1234).bar());` as the expression is not ended yet. – user2864740 Sep 12 '20 at 01:10
  • 1
    The lesson here is that "it works fine" can be one possible (unreliable) manifestation of undefined behavior. – Drew Dormann Sep 12 '20 at 01:11

1 Answers1

7

On this line:

const char * s = foo(1234).bar();

s is pointing to the char s[4] member of a temporary foo object. At the end of the full expression, this object dies, and s is now pointing to invalid memory. Printing it on the next line will invoke undefined behavior.


The issue here is with the use of the return value of bar(). The chained function call itself is fine, so if you use the expression directly in the printf it's well-defined:

printf("%s\n", foo(1234).bar());  // ok
cigien
  • 57,834
  • 11
  • 73
  • 112
  • how about auto&& s=foo(1234).bar();? would that still be undefined behavior? – Hikmat Farhat Sep 12 '20 at 01:31
  • 5
    @HikmatFarhat yes that is still UB, references only extends lifetime of objects they refer to, not function return pointer of a temporary internal buffer. Ie `auto && f = foo(); const char *s = f.bar();` is better – Slava Sep 12 '20 at 01:37