23

It took me a while to figure out why some cout output seem to disappear into the ether. The culprit:

std::cout<< "This line shows up just fine" << std::endl;
const char* some_string = a_function_that_returns_null();
if (some_string == 0)
    std::cout<< "Let's check the value of some_string: " << some_string << std::endl;

std::cout<< "This line and any cout output afterwards will not show up" << std::endl;

The output from the snippet above will be:

This line shows up just fine
Let's check the value of some_string:     

So feeding a NULL into cout will disable all output afterward. Why? And how to fix it?

This doesn't happen all the time - a co-worker with the same code gets all the expected output. And in case you wonder why I can't just prevent feeding NULL into cout with a if statement: I'm working in a large codebase, and don't know where else this happens! All I know is the cout statements I put never showed up.


More info:

a_function_that_returns_null() is actually getenv("HOST"). I checked on the commandline via echo $HOST that the HOST variable is empty. If I do export HOST= (bash flavor), the output are all there. I have no idea what the HOST variable contains initially nor what getenv returns initially when before I modify the HOST variable; all I know is (some_string == 0) is true.

polyglot
  • 9,945
  • 10
  • 49
  • 63
  • What version of GCC, on what platform? – ildjarn Aug 11 '11 at 00:16
  • 4
    The reason it happens is because it seems to put cout into an error state. I haven't yet worked out whether it *should* do that or not. If you put `std::cout.clear()` in there, it seems to fix it. – Benjamin Lindley Aug 11 '11 at 00:18
  • @Benjamin: It's likely to fix it, but unless `clear()` can manage to clean up whatever damage has been done, it might just leave it in a slightly less messed-up state. – Keith Thompson Aug 11 '11 at 00:21
  • 3
    @Benjamin: It's already UB to stream `(char const*)0`. `[2003: 27.6.2.5.4/3]` – Lightness Races in Orbit Aug 11 '11 at 00:28
  • @Tomalak: Yes, I see it now, I was not sure, which is why I didn't answer. – Benjamin Lindley Aug 11 '11 at 00:32
  • @Benjamin: Makes sense! I think you're probably right when it comes to the OP's stdlib impl, though. – Lightness Races in Orbit Aug 11 '11 at 00:33
  • @Benjamin: std::cout.clear() seems to work, so if you put that as an answer I'll accept it. I'm not really looking for how to fix the NULL problem (that's in someone else's code) but to work around it enough so that I can code my own stuff with proper cout output. – polyglot Aug 11 '11 at 00:39
  • @Benjamin: Confirmed that you are, in my answer. – Lightness Races in Orbit Aug 11 '11 at 00:39
  • 2
    @polyglot: No, you _must_ stop doing this. That your stdlib works around the issue is **pure chance** and may change at any time. – Lightness Races in Orbit Aug 11 '11 at 00:39
  • @Tomalak: Obviously I won't do it myself, but someone else did, and to go around and fix all the instances in the code is beyond my time limits at this moment... – polyglot Aug 11 '11 at 00:43
  • 1
    @polyglot: No, I will not put that as an answer. Tomalak is right. – Benjamin Lindley Aug 11 '11 at 00:43
  • 2
    @polyglot: Trust me when I tell you that you are saving yourself both a tremendous amount of time and a tremendous amount of future work if you fix this properly rather than relying on some fortunate result of UB. This is _not_ the sort of thing you take shortcuts with! – Lightness Races in Orbit Aug 11 '11 at 00:44
  • @Benjamin & Tomalak: alas as I mentioned, I'm dealing with a large codebase with 0 line of my own code so far (I'm just studying it with lots of cout before I do any coding). I don't have the time to go through and fix the problems properly every place. `cout.clear()` does the trick for now, but I'll certainly let someone else know the problem or fix it myself later. – polyglot Aug 11 '11 at 00:52

3 Answers3

30
const char* some_string = a_function_that_returns_null();

Do you mean that it literally returns a null pointer?

[2003: 27.6.2.5.4]:

template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const char* s);

3. Requires: s is non-null.

Then streaming some_string is Undefined Behaviour; you cannot dereference a pointer to get a string — even an empty one — if the pointer is not valid.


This doesn't happen all the time - a co-worker with the same code gets all the expected output

UB leads to unreliable symptoms. That you don't always get a crash can be slightly surprising because most modern OSs make a point of always SIGSEGVing when you try to dereference a null pointer.

However, from a C++ point of view, anything can happen; in your particular case, your standard library implementation may well be checking for a null pointer and setting an error flag on the stream instead of attempting to dereference the pointer. That is its prerogative.

(It's also probably why your subsequent stream operations are failing: attempting to write to a stream does nothing when there's an error flag set.)

For example, the libstdc++ that ships with GCC 4.6.0, despite naming s != 0 as a precondition, does do this:

00325       if (!__s)
00326     __out.setstate(ios_base::badbit);
00327       else

However, you must not rely on this behaviour; it could change at any time!


So, simply don't do this. Stream a valid, but empty, string if you really must.

And what's wrong with std::string?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Now I didn't make that mess, so don't blame me :) I modified the question to contain some more information on a_function_that_returns_null(). Thanks for the extra information though! It's good to know why. – polyglot Aug 11 '11 at 00:37
  • 1
    @polyglot: Well, you did. You should have checked the result of your `getenv("HOST")` call before streaming it to `cout`. :) – Lightness Races in Orbit Aug 11 '11 at 00:41
  • 1
    The `getenv("HOST")` is *not* my code :) I just go around and put enough `cout <<__LINE__ << endl` to figure out where the cout stop outputing. – polyglot Aug 11 '11 at 00:44
  • @polyglot: You posted the question so, as far as we're concerned, it is your code. :) – Lightness Races in Orbit Aug 11 '11 at 00:47
  • I don't think that you can't construct a `std::string` from a null pointer either, so `std::string` isn't _quite_ the easy fix you suggest. – Mooing Duck Jan 25 '13 at 18:13
  • @MooingDuck: It follows the suggestion to switch to an empty string if needed. That is, this situation would never even have arisen were `std::string` used. – Lightness Races in Orbit Jan 25 '13 at 18:17
  • `cout << __LINE__ << endl;` to print the line number is a good idea. I didn't know that trick. Thanks. – Galaxy May 06 '19 at 01:47
8

I'm fairly sure that cout << (char*)NULL has undefined behavior. I'm afraid that "Don't do that" is the best advice I can offer.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

When you are trying to output const char*, the stream prints all the bytes until it reaches '\0'. It is leading to undefined behavior. For example, you could print some control symbols (i.e. '\n','\r' and so on) and get unpredictable result.

EDIT: Actually streaming of NULL pointer is enough to get UB. I'm not going to delete my answer due to useful comments.

eugene_che
  • 1,997
  • 12
  • 12
  • Dereferencing the null pointer, however, should result in a crash. – Arafangion Aug 11 '11 at 00:18
  • @Arafangion: Dereferencing a null pointer results in undefined behavior. I agree that it *should* result in a crash, but there's no guarantee that it actually will. Some part of the system might be trying to do you a favor by handling the condition. – Keith Thompson Aug 11 '11 at 00:20
  • @Keith: Possible, but unlikely, but I will agree that we should at least assume that it's always the case. – Arafangion Aug 11 '11 at 00:21
  • @Arafangion: You may not rely on UB to crash the process. _Anything_ can happen. You only get a crash if you're lucky. (That said, modern OS's _do_ tend to reliably segfault on null pointer references >.<) – Lightness Races in Orbit Aug 11 '11 at 00:21
  • @Arafangion: er, dereferences* :) – Lightness Races in Orbit Aug 11 '11 at 00:30
  • @Arafangion: Sorry, what exactly should we assume is always the case? Whatever it is, I suggest that no, we shouldn't assume anything. – Keith Thompson Aug 11 '11 at 01:23
  • @Keith: I'm inclined to agree, but do note that when I say "should", I mean just that, not that it's guaranteed to do so - if I meant that, I would've said "must". :) – Arafangion Aug 11 '11 at 01:24
  • @Arafangion: I agree with your first comment, that it "should result in a crash". I disagree with your later comment, that "we should at least assume that it's always the case" (though I'm not quite sure what "it" refers to). – Keith Thompson Aug 11 '11 at 01:27
  • @Keith: "it" being your assertion that it's undefined behaviour. :) – Arafangion Aug 11 '11 at 01:31