Does fscanf move the file pointer backwards?

1.2k views Asked by At

These are the contents of my file, 'unsorted.txt' :

3 robert justin trump

This is my code:

#include <stdio.h>

int main(void) {
    FILE *f = fopen("unsorted.txt", "r");
    char n;
    printf("%d\n", ftell(f));
    fscanf(f, "%s", &n);
    int l = n - '0';
    printf("%d %d\n", l, ftell(f));
    return 0;
}

on execution it gives the following output:

0
3 -1

why did it return -1 in second case? It should move from 0 to 1 right?

NOTE: the file can be opened, because then how would it print 0 in the first call and the first character from the file without being able to be opened?

2

There are 2 answers

0
Basile Starynkevitch On BEST ANSWER
 fscanf(f,"%s",&n);

is very wrong, since you declared char n; (of only one byte). You got undefined behavior. Be very scared (and next time, be ashamed).

I recommend:

Test that fopen don't fail:

FILE *f = fopen("unsorted.txt","r");
if (!f)  { perror("fopen unsorted.txt"); exit(EXIT_FAILURE); };

Declare a buffer of reasonable size (80 was the size of punched cards in the 1970s).

char buf[80];

clear it (you want defensive programming):

memset(buf, 0, sizeof(buf));

Then read carefully about fscanf. Read that documentation several times. Use it with a fixed size and test its result:

if (fscanf(f, "%72s", buf) > 0) {

(72 was the usable size in PL/1 programs of punched cards; it is less than 80)

Don't forget to read documentation of other functions, including ftell.

Important hint:

compile with all warnings and debug info (gcc -Wall -Wextra -g with GCC), improve the code to get no warnings, use the debugger gdb to run it step by step.

PS. As an exercise, find the possible content of unsorted.txt which made your initial program run correctly. Could you in that case predict its output? If not, why??

0
chqrlie On

There are multiple problems in your code:

  • You do not test the return value of fopen(). Calling ftell() with a NULL pointer has undefined behavior. You cannot draw conclusions from observed behavior.

  • printf("%d\n", ftell(f)); is incorrect because the return value of ftell() is a long. You should use the format %ld.

  • fscanf(f, "%s", &n); is incorrect because you pass the address of a single char for fscanf() to store a null-terminated string. fscanf() will access memory beyond the size of the char, which has undefined behavior. Define an array of char such as char buf[80]; and pass the maximum number of characters to store as: fscanf(f, "%79s", buf); and check the return value, or use %c to read a single byte.

  • int l = n - '0'; is not strictly incorrect, but it is error prone: avoid naming a variable l as it looks confusingly similar to 1.

  • printf("%d %d\n", l, ftell(f)); is incorrect as the previous call to printf: use the conversion specifier %ld for the return value of ftell().

Note also that the return value of ftell() on a text stream is not necessarily the byte offset in the file.

Here is a corrected version:

#include <stdio.h>

int main(void) {
    FILE *f = fopen("unsorted.txt", "r");
    char c;

    if (f != NULL) {
        printf("%ld\n", ftell(f));
        if (fscanf(f, "%c", &c) == 1) {
            int diff = c - '0';
            printf("%d %ld\n", diff, ftell(f));
        }
    }
    return 0;
}

Output:

0
3 1