2

I am curious to know that, how fgetc() function works. How internal file pointer automatically advance.

#include <stdio.h>
int main() {
    char d[10];
    int i;
    FILE *f=fopen("explore.ttt", "r");
    while(!feof(f)) {
        d[i]=fgetc(f);
        i++;
    }
    printf("%s", d);
    return 0;
}

What logic works behind fgetc() function.

Fabio
  • 336
  • 3
  • 17
Aman Warudkar
  • 125
  • 1
  • 1
  • 12
  • 3
    `while(!feof(f)) { d[i]=fgetc(f);` this is the wrong construct. Check the return value of `fgetc()` instead of `feof()`. – chux - Reinstate Monica Oct 16 '17 at 05:53
  • 4
    See [Why is “while ( !feof (file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong/26557243) – Marco Oct 16 '17 at 05:55
  • 2
    The most useful "model" for library functions (which are platform specific, compiler specific, implementation specific, ....) is "magic". – Yunnosch Oct 16 '17 at 06:00
  • 1
    And also [read the return value into an `int`](https://stackoverflow.com/questions/35356322/difference-between-int-and-char-in-getchar-fgetc-and-putchar-fputc) – Antti Haapala -- Слава Україні Oct 16 '17 at 06:01
  • 1
    Also, on POSIX systems the operating system itself advances one pointer; and for buffered streams the libc has *another* pointer. – Antti Haapala -- Слава Україні Oct 16 '17 at 06:02
  • 2
    Take a look at Plauger's "The Standard C Library" book. It discusses the implementation of the entire C library (circa 1992). It's meant for study and is completely annotated. – Michael Burr Oct 16 '17 at 07:44
  • 1
    Note, tracing the old `fgetc` and `getc` function source through the current Gnu source is rather frustrating. The both are no longer in `stdio` (as of 7-8? years ago) there were moved into the bowels of libio. With a bit or persistence, you can ferret the code out. – David C. Rankin Oct 16 '17 at 07:45
  • 1
    If your library has Plauger, I second the recommendation. An oldie but goodie, especially if you have to maintain code written for C89. – Davislor Oct 16 '17 at 07:57

1 Answers1

4

This is an implementation detail that will vary from compiler to compiler. However, one popular implementation whose source code is free to download is GNU libc, so let’s look at that.

The libc package defines fgetc() as part of libio, as an alias to an internal function:

weak_alias (_IO_getc, fgetc)

(Recall that the standard says functions such as fgetc must be macros that can be undefined to give you the name of a function, whose address can be taken.) The definition of _IO_getc is:

int
_IO_getc (FILE *fp)
{
  int result;
  CHECK_FILE (fp, EOF);
  if (!_IO_need_lock (fp))
    return _IO_getc_unlocked (fp);
  _IO_acquire_lock (fp);
  result = _IO_getc_unlocked (fp);
  _IO_release_lock (fp);
  return result;
}

So this is a wrapper for _IO_getc_unlocked(), which turns out to be a macro defined in libio.h:

#define _IO_getc_unlocked(_fp) \
       (_IO_BE ((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end, 0) \
    ? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++)

So, what this implementation does is define a field of the FILE structure that it calls _IO_read_ptr. The underlying macro that fgetc() is a wrapper for dereferences this pointer and then increments it.

Other libraries may do something different, but the basic idea will be the same. Call some lower-level function to read from the file (such as read() on POSIX), or if it’s important to be portable across different OSes, a wrapper that hides the differences. Maintain some kind of seek pointer or offset in the FILE structure that you reference when you pass a FILE*, update, dereference and increment.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • 1
    Much of this just accommodates and internal I/O buffer of BUF_SIZE chars which are read from the file making BUF_SIZE chunks of characters available for _IO_read_ptr to traverse. Unix/Linux (`BUF_SIZE 8192`) on windows (or using MinGW) (`BUF_SIZE 512`). – David C. Rankin Oct 16 '17 at 07:41
  • 1
    Yes. The actual return value is from the ternary `?``:` operator, with an expression that dereferences, casts and post-increments `_IO_read_ptr++` if there is data to read. – Davislor Oct 16 '17 at 07:45
  • 2
    Interestingly the, e.g. `fgetc_unlocked()` functions are made available, if defined through `stdio.h`. Those were also fun to chase down in the source `:)` – David C. Rankin Oct 16 '17 at 07:50