Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not able to understand what this C function does

Tags:

c

pointers

I have seen this piece of code several times but am unable to understand what it does

inline char nc()
{
    static char buf[100000], *L = buf, *R = buf;
    return L == R && (R = (L = buf) + fread(buf, 1, 100000, stdin), L == R) ? EOF : *L++;
}

The condition L==R should always be true , right? since both the pointers point to the same variable. I cant understand what the second part of the condition checks. Could someone please help me out?

like image 394
rohit_r Avatar asked Dec 01 '22 09:12

rohit_r


2 Answers

All the variables are declared as static which means that they keep their value from the previous function call. The initializations =buf are only run the first time the function is called. It's basically the same thing as if you declared those variables as globals and initialized them before the first function call. Of course with the difference that globals can be accessed from anywhere in the code.

Let's disassemble the second line a bit. It can be rewritten like this:

char ret;
if(L == R && 
   R = (L = buf) + fread(buf, 1, 100000, stdin), L == R) {
       ret = EOF;
} else {
    ret=*L; // Return the character read
    L++;    // Advance to next character in the buffer
}
return ret;

A bit clearer, but still a bit clunky. The second condition R = (L = buf) + fread(buf, 1, 100000, stdin), L == R) is not very clear. It can be rewritten like this:

L = buf;
int noCharactersRead = fread(buf, 1, 100000, stdin);
R = L + noCharactersRead;
if(L == R)  // If no characters have been read
    ret = EOF;

So the complete refactored code (with some extra refactoring) will be

char nc()
{
#define BUFSIZE 100000
    static char buf[BUFSIZE], *L = buf, *R = buf;
    if(L == R) { // If buffer is empty
        L = buf; // Reset L to beginning of buffer

        // Read next character from stdin, put it in the buffer
        // and check if read was successful
        int noCharactersRead = fread(buf, 1, BUFSIZE, stdin);

        // Return EOF on read failure
        if(noCharactersRead == 0)
            return EOF;

        // Advance R one step if a character was read
        R = L + noCharactersRead;

    } 

    // If the buffer was not empty, or if the buffer was empty and we
    // successfully read a new character into the buffer, return the next
    // character in the buffer and advance L
    return *L++; 
}

I removed the inline because the function contains static variables. An alternative would be to declare the function as static inline.

It is essentially a buffered version of getchar() function, but written in a very unreadable way. Do also note that it has very little protection for buffer overflow. It basically relies on the buffer being big enough to not cause any issues. A way to solve this would be changing the call to fread to fread(buf, 1, BUFSIZE - (R-L), stdin)

like image 106
klutt Avatar answered Dec 04 '22 22:12

klutt


This is basically equivalent of:

char buf[100000];
char* L = buf;
char* R = buf;

char non_fancy_getchar()
{
    if (L == R) {
        /* reset pointers and try to read stdin again */
        L = buf;
        R = L + fread(buf, 1, sizeof(buf), stdin);
    }

    /* return next char or EOF */
    return (L == R) ? EOF : *L++;
}
like image 42
Matt Avatar answered Dec 04 '22 22:12

Matt