Search code examples
cstdiofile-pointerungetc

error when mixing calls to fgetc, ungetc() and fputc() witht the same file


This code mix calls to putc() and ungetc() on the same file:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fp;
    int c;

    fp = fopen("filetest.txt", "r+");

    int fno = fileno(fp);


    /* get and print first position '1' */
    if((c = fgetc(fp)) == EOF){
        printf("fgetc(): error\n");
    }else{
        printf("%c\n", c);
    }

    /* get 2nd position '2' */
    if((c = fgetc(fp)) == EOF){
        printf("fgetc(): error\n");
    }

    /* put char 'X' in the 3rd position*/
    if((fputc('X', fp)) == EOF){
        printf("fputc(): error\n");
    }


    /* -- problematic code --- */
    /* Unget '2' */
    if (ungetc(c, fp) == EOF){
        printf("ungetc(): error\n");
    }

    /* get again and print '2' */
    if((c = fgetc(fp)) == EOF){
        printf("fgetc(): error\n");
    }else{
        printf("%c\n", c);
    }
    /* -- end of problematic code --- */


    /* get 4rd position '4' */
    if((c = fgetc(fp)) == EOF){
        printf("fgetc(): error\n");
    }else{
        printf("%c\n", c);
    }

    if (fclose(fp) != 0){
        printf("fclose(): error\n");
    }

}

Being the contents of the 'filetest.txt':

12345

I thought the output would be:

1
2
4

but instead I got an error in fgetc():

1
2
fgetc(): error
fclose(): error

If the part that says "problematic code" is removed, the output is ok:

1
4

why is it failing?

Since stackoverflow doesn't allow me to post it (too much code and too little text explanations) here is some filler text of what I have tried:

  • reading the man pages for getc, fgetc, fputc, and ungetc
  • originally I was using putc, getc so I changed to fputc and fputc to discard macro errors (I was just desesperate)

The next is to read the implementation of getc, ungetc and putc but I was wondering if someone could help explain me this before that...


Solution

  • here is some filler text of what I have tried:

    • reading the man pages for getc, fgetc, fputc, and ungetc

    I suggest that you also read the man page for fopen, as it specifies what you are doing wrong:

    When a file is opened with update mode ('+' as the second or third character in the mode argument), both input and output may be performed on the associated stream. However, the application shall ensure that output is not directly followed by input without an intervening call to fflush() or to a file positioning function (fseek(), fsetpos(), or rewind()), and input is not directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

    Since your program is violating this rule, it has undefined behavior.

    To fix this, it is sufficient to use the apparent no-op statement

    fseek( fp, 0, SEEK_CUR );
    

    when changing from reading to writing and vice-versa.