0

While working on a project in C and gcc, I've come up on something quite strange I can't understand. The following code should print the value of i, at least once, but for some reason, on Linux and gcc (also g++), it does not, it just hangs without outputting anything. Note printf does indeed work in other scenarios.

#include <stdio.h>

int main() {
    int i;
    int j;
    int z;
    for (i = 0; i < 47966; i++) {
        printf("%d ", i);
        for (j = 0; j < 47966; j++) {
            for (z = 0; z < 47966; z++) {
            }
        }
    }
    return 0;
}

Has anyone encountered this? Why is this happening?

jww
  • 97,681
  • 90
  • 411
  • 885
user1176999
  • 440
  • 8
  • 20
  • 4
    It is probably being buffered and never flushed since you don't have any new lines. You can force a flush using `fflush(stdout);`. – Mogzol Sep 24 '18 at 20:41
  • Ah, yes, brilliant. Thank you very much, that was the problem. – user1176999 Sep 24 '18 at 20:45
  • printing a \n somewhere will flush the buffer – Katajun Sep 24 '18 at 20:50
  • @Mogzol Can you please explain your comment? – Gaurav Sep 24 '18 at 21:04
  • 2
    @Observer By default in most operating systems, stdout is line-buffered, meaning all data written to it is buffered in memory until a newline character is sent. The consequence of this is that if you never send a newline, your data will just continue being buffered until something finally forces stdout to flush. Calling fflush is one way that we can force this flush. When it is flushed, all the characters in the buffer are finally written to stdout and show up in our terminal (or wherever stdout is pointing to). – Mogzol Sep 24 '18 at 21:14
  • @Mogzol How is the line buffered part relevant here - isn't printf printing one value of `i` at a time and then the outer two loops have not effect? – Gaurav Sep 24 '18 at 21:19
  • 1
    @Observer Yes that is correct, but remember that printf doesn't print a newline unless you explicitly tell it to (i.e. put `\n` at the end of the string). So the program in the OP will print "1 2 3 4 ..." all on one line, and never print a newline character, thus never flushing the buffer. – Mogzol Sep 24 '18 at 21:22
  • @Mogzol Let's say we don't use the other loops and neither do we use a new line - then also the value gets printed all the values are getting buffered from i=0 to i=47966 - at once without encountering a newline. If the buffer is just full and then it just vomits the data on the screen in the case I mentioned then why it doesn't happen in the question as well? – Gaurav Sep 24 '18 at 21:53
  • @Observer the size of the stdout buffer is implementation-defined. It's entirely possible that the buffer will be large enough to hold that entire line. Or it might not be and will eventually flush itself, as you say. It's entirely dependent on the implementation, and can vary from one machine (or environment) to another. – Mogzol Sep 24 '18 at 22:53
  • 1
    @Observer Because if the code is *unoptimized* it's 2,300,737,156 iterations between each print which is about one print every few seconds on my laptop. That will take quite a while to fill even a modest buffer. If it's optimized it will optimize away the inner loops and execute immediately. – Schwern Sep 24 '18 at 23:20
  • No problem here.(except for nonsensical output ...) Maybe you are exposed to Microsof *sofware* ? BTW: **what** is happening? – wildplasser Sep 24 '18 at 23:28
  • @katamari absence of `\n` is not the issue here. – Gaurav Sep 25 '18 at 00:06

2 Answers2

2

You are likely Suffering From Buffering.

It is more efficient in most cases to write to a device in large chunks rather than in lots of little ones. Writing to a filehandle, and printf is writing to stdout, is typically held in a memory buffer before being sent to the actual device. That buffer must be "flushed" meaning the contents of the buffer are written to the device. There are three types of buffering: unbuffered, block buffered, and line buffered.

stderr is typically unbuffered, every write to stderr is immediately sent to the device. This is good because you want to see your error information right away.

Files are typically block buffered. Writes are stored in a memory buffer of BUFSIZ and flushed when the buffer is reached or when the filehandle is closed either explicitly or when the process terminates.

stdout is line buffered meaning it has a buffer, but automatically flushes when it sees a newline or when something is read from stdin. This is a compromise between usability and performance, typically you want to display entire lines at a time or when you're prompting for input.

Your program never outputs a newline, so your numbers will stay in the line buffer until the buffer is full or the process terminates. Since it takes 2,300,737,156 iterations to print a single integer it'll be a while before the buffer is filled. And 110,357,158,424,696 iterations before the program terminates and automatically flushes and closes stdout. Though if you compile it with optimizations the compiler will recognize the internal loops do nothing and eliminate them; then your code will execute and print very quickly.

You can solve this by flushing the buffer manually just after you print with fflush(stdout).

Schwern
  • 153,029
  • 25
  • 195
  • 336
1

There are only three cases when output is sent from the buffer to the screen:

1: It is sent when the buffer gets full.

2: When a newline character is encountered (in a line buffered terminal).

3: When there is impending input.

Unlike from what has been said by @Mogzol in the comments, if there was some buffering problem going out here then this simple single loop wouldn't have worked either:

for(i=0;i<47966;i++)
{
    printf(" %d",i);
}

There is something else happening in this, step by step example:

CASE 1:

#include<stdio.h>

int main(void)
{   
    unsigned long int i=47966ull,k;

    for(k=0;k<i;++k)
    {

    }

    return 0;
}

This above code takes nearly negligible time to complete, although nothing is done by the loop.

CASE 2:

#include<stdio.h>

int main(void)
{
    unsigned long int i=47966,k;

    for(i=0;i<47966;i++)
        for(k=0;k<47966;++k)
            {

            }

    return 0;
}

This above code nearly takes 9.1 seconds (not counting the error) to run while doing nothing.

CASE 3:

#include<stdio.h>

int main(void)
{   
    unsigned long int i=47966,k,z;

    for(i=0;i<47966;i++)
    {
        printf(" %d",i);
        for(k=0;k<47966;++k)
        {

        }
    }

    return 0;
}

This certainly works as from case 1 we can say that for each i the waiting time is less, hence the digits get printed soon.

Even to print this simple underlying code (you can try that), it takes a massive amount of time:

//WRITTEN TO CHECK WHEN DOES THE FIRST OVERFLOW OCCURS
#include<stdio.h>

int main(void)
{
    unsigned long int i=47966,k,z;

    for(i=0;i<47966;i++)
    {
        printf(" %d",i);
        for(k=0;k<47966;++k)
        {
            printf("-");
        }
    }

    return 0;
}

In the case of yours, its a massive waiting time before the first buffer overflow occurs. I have mentioned overflow because:

  • Even your terminal is set to line buffer there is no \n to flush it.
  • There is no impending input.

SOURCE(S): - What is it with printf() sending output to buffer?

NOTE: Ignore the unsigned long part - it was just from some old program I didn't bother to change.

MAJOR--> If you use fflush(stdout) in the inner most loop, you'll find that it is just timing issue - consuming lots of time to buffer all of them from 0 to 47966 as there would be only one number in the buffer between two consecutive flush.

Gaurav
  • 1,570
  • 5
  • 17