The importance of std::endl in C++

As it turns out, it’s a bit more than a verbose \n

The semester is wrapping up, and so like many students, I’m rushing to finish projects that I could have started weeks ago, but elected not to. I spent the past few years as a teacher, constantly getting on students' cases about them waiting too long to start working on projects… turns out I’m no better!

Anyway, in the process of working on one of these projects, I made an interesting discovery about C++ and how important std::endl is when you are using std::cout to produce program output.

The assignment in question involves a writing distributed shared memory system, and then verifying that it works using some provided test clients. In particular, I was fighting with a distributed sort-merge implementation when I noticed some strange behavior regarding my output.

To verify that it works properly, the test client writes the sorted array to stdout using something like the following,

for (int i = 0; i < ELEMENT_CNT; i++)
  std::cout << shared_array[i] << "\n";

Once I got it to a point where it more-or-less worked, I noticed that the output wasn’t quite right. The sorted array was correct in memory, but when I redirected stdout into wc to count the size of the output, it consistently came up a little short.

For example, when sorting an array of 16,384 elements, the actual size of the output would be either 16,264 or 16,263, and occasionally the last number in the file would be missing digits (e.g., 4 instead of 499).

Being a C-guy (who begrudgingly is required to use C++), I added an fprintf call to verify the number of elements actually being written,

for (int i = 0; i < ELEMENT_CNT; i++) {
  std::cout << shared_array[i] << "\n";
  fprintf(stderr, "%d\t%d\n", i, shared_array[i]);
}

and, lo and behold, they were all there!

At this point, I was reasonable sure that it was a buffer-related issue. It looked a lot like my output was being stuck in a buffer and then not flushed properly before the program terminated.

And that’s exactly what it was. As it turns out, the problem is that the provided test code isn’t using std::cout quite correctly.

std::endl is something I’ve seen associated with most calls to std::cout in the wild, but I always assumed it was just a long way of saying, “put a newline here” (like Environment.NewLine in C#). Upon finally pulling up the documentation on it, though, I found that there is an important difference.

std::endl

Inserts a new-line character, and flushes the stream 1

It would seem that data written to std::cout isn’t necessarily flushed until an std::endl comes along.

Replacing the \n from the test code with std::endl, like so,

for (int i = 0; i < ELEMENT_CNT; i++)
  std::cout << shared_array[i] << std::endl;

fixed the problem immediately and completely.

So, there you have it! If you’re going to use std::cout in C++, you definitely want to be using std::endl rather than \n. Personally, I’m content (at least for now) to leave a #include <cstdio> at the top of my C++ files, but even so, that was definitely a useful lesson to learn.


  1. https://www.cplusplus.com/reference/ostream/endl/ ↩︎

Related