jlm-blog
~jlm

29-Dec-2016

A puzzle about C’s stdio

Filed under: programming — jlm @ 18:06

I found the C puzzles webpage by Gowri Kumar to be a very interesting collection of oddities of the C language and some of its basic libraries. If you work with C for fun or profit, I encourage you to go and give them a try. I found very few of them to produce behavior I hadn’t expected, which could be a symptom of overfamiliarity with C. I did find a few surprises though, which I felt warranted further investigation.

Consider one of Gowri’s earlier puzzles: [full source]

int main() {
    while(1) {
        fprintf(stdout,"hello-out");
        fprintf(stderr,"hello-err");
        sleep(1);
    }
    return 0;
}

This behaved as I expected. However, one of the later (near the middle) examples was [full source]:

int main() {
    char str[80];
    printf("Enter the string:");
    scanf("%s",str);
    printf("You entered:%s\n",str);
    return 0;
}

Gowri asks “What is the potential problem with the [] program?” (which is hopefully obvious), yet I was actually surprised by the program’s actual behavior when the problem wasn’t encountered, as it seemed to conflict with that earlier puzzle. This conflict is accentuated by removing the read into a buffer [full source]:

int main() {
    for (;;) {
        printf("Gimme a number! ");
        sleep(1);
        scanf("%*d");
    }
    return 0;
}

Can you predict how this program will behave? And explain why?
What about a slightly more complicated case? [full source]

int main(int argc, char **argv) {
    int arg1val = (argc > 1) ? atoi(argv[1]) : 0;
    FILE *outf = (arg1val & 1) ? fdopen(dup(fileno(stdout)), "w") : stdout;
    FILE *inf = (arg1val & 2) ? fdopen(dup(fileno(stdin)), "r") : stdin;

    for (;;) {
        fprintf(outf, "Gimme a number! ");
        sleep(1);
        fscanf(inf, "%*d");
    }
    return 0;
}

Does your explanation hold up to this example’s behavior when called with an argument of 1, 2, and 3? What is this program doing in each of those three cases? If you trace the system calls the program makes with an argument of 1, 2, and 3, does that match the behavior you predicted?
I found all this to be pretty counterintuitive!


Update: Here’s a version which shows more of what’s going on. Try running it with “0”, “1”, and “5”, for example. Or piping the output of “echo 1 2 3 4” to it.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress