Overloaded ostream operator segmentation fault if no endl
Asked Answered
D

1

7
class foo {
    public:
    friend ostream& operator << (ostream &os, const foo &f);
    foo(int n) : a(n) {}
    private:
    vector <int> a;
};

ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    os << endl; // why is this line a must?
}

int main(void) {
    foo f(2);
    cout << f << endl;
    return 0;
}

In the above code, if the marked line is removed, there will be a segment fault error, can someone explain why?

Dravidian answered 3/4, 2013 at 13:27 Comment(1)
Why nobody ever cares to check the code in question? Compiler should have warned you about such error -"... warning: no return statement in function returning non-void [-Wreturn-type]" See liveworkspace.org/code/2ygK20$1 } ^Nonprofit
R
19
ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    os << endl; // why is this line a must?
}

is not manadatory. The segfault is caused because you are not returning os

ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    return os; // Here
}

it is undefined behavior if you don't return the ostream. The endl is flushing your os here. That's why it seems like it is working.

EDIT: Why it is working in this case according to Bo Persson

The os << endl; is another operator call that actually returns os by placing it "where a return value is expected" (likely a register). When the code returns another level to main, the reference to os is still there

Rabbi answered 3/4, 2013 at 13:33 Comment(15)
Is the implicit int (?) which would be returned if nothing is specified or its value somehow standardized?Urba
There is no implicit int returned here. It is quite clearly specified as returning ostream&. So without a return statement you get whatever junks happens to be in the right place on the stack after the function. It just so happens that with the os<<endl there, that junk is something that doesn't cause a crash.Ernaernald
@honk There is no implicit int in C++. Failing to return something from a non-void function is simply undefined behaviour, period.Boeotian
Thanks @Angew, that's the answer to my question.Urba
@stardust_ "The endl in this case..." That's not really true. That line is likely placing os where the return value is expected. By coincidence.Bilbao
@Ernaernald Wait... is that always true for any function? You don't have to return what you said you would? It's the same as just leaving a variable uninitialized (or UB)? Isn't it a compile error?Louls
@Dave: This would trigger a warning e.g. in GCC with -Wall.Urba
@Dave You do have to. Just some compilers with whatever options might not tell you that you've done it wrong.Ernaernald
Well that's stupid for any compiler not to treat it like an error. There's no edge case where it's not an error. It's definitely wrongLouls
@Dave. the thing is it is not technically an error if you don't use the return value. If you jsut call the function and not assign the result to anything. However in an ostream case you are always using what is returned (because of the syntax).Rabbi
From N3337 (first draft after c++11) 6.6.3: "Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function." I guess the reason a compiler might not consider this an error is that there are other legal ways to exit a function (e.g. throwing an exception).Ernaernald
@Dave - The os << endl; is another operator call that actually returns os by placing it "where a return value is expected" (likely a register). When the code returns another level to main, the reference to os is still there.Manard
@Dave: int foo() { for (;;) { int i; if (std::cin >> i) { return i; } std::cin.clear(); } } is a perfectly conforming function even though there is no return at the end. It is only undefined behavior if you exit the function "normally" without returning. Compilers do not make this a hard error because they cannot, in general, prove that you will go down this path; after all you may be calling a function that throw or something...Bosh
@MatthieuM. So is it UB by exiting without a return or is it only UB if you try to use the return value from a function didn't return anything?Louls
@Dave: in C it's UB if you try to use the value, in C++ it's UB if you "fall off" the end of the function without returning.Bosh

© 2022 - 2024 — McMap. All rights reserved.