无法理解此C函数的作用

时间:2019-05-26 13:33:28

标签: c pointers

我已经看过几次这段代码,但是无法理解它的作用

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++;
}

条件L==R应该始终为true,对吗?因为两个指针都指向同一个变量。我不明白条件检查的第二部分。有人可以帮我吗?

4 个答案:

答案 0 :(得分:5)

所有变量都声明为static,这意味着它们保留了上一个函数调用中的值。初始化=buf仅在第一次调用该函数时运行。基本上就和在第一个函数调用之前将这些变量声明为全局变量并将其初始化一样。当然,可以从代码中的任何位置访问全局变量。

让我们稍微拆开第二行。可以这样重写:

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;

更加清晰,但仍然有些笨拙。第二个条件R = (L = buf) + fread(buf, 1, 100000, stdin), L == R)不太清楚。可以这样重写:

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

因此完整的重构代码(带有一些额外的重构)将会

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++; 
}

我删除了inline,因为该函数包含静态变量。一种替代方法是将该函数声明为static inline

它本质上是getchar()函数的缓冲版本,但是以非常不可读的方式编写。还要注意,它对缓冲区溢出的保护很小。它基本上依赖于缓冲区足够大而不会引起任何问题。解决此问题的一种方法是将对fread的调用更改为fread(buf, 1, BUFSIZE - (R-L), stdin)

答案 1 :(得分:5)

这基本上等同于:

MapView

答案 2 :(得分:5)

这是一个认为自己很聪明的人写的一个坏函数。

对象bufLRstatic定义,它们在程序执行期间一直存在,并在执行开始时进行初始化。它们在调用函数之间保留其值。

名义上,该函数返回一个字符(*L++)或EOF。但是返回类型为char,返回EOF的例程不应使用该类型。 EOF被指定为int值,并且不是char值,而是与char值重叠(这是不希望的,因为这样就无法区分它们,例程应该将unsigned char用于buf),或在转换为char值时引发其他问题(转换将发出信号或与char值重叠)。

意图是L(对于“左”)指向已读入缓冲区但尚未使用的字符的左侧,而R指向右侧(末尾)已读入缓冲区的字符。当L等于R时,缓冲区为空,应向其中读取更多字符。

L(代表“左”)和R(代表“右”)相等时,将计算&&的右边操作数。我们可以重写它:

(R = (L=buf) + fread(buf,1,100000,stdin), L==R)
    ? EOF
    : *L++;

这会将L重置为缓冲区的开始,然后尝试读取10000个字符。实际读取的字符数将添加到L,然后分配给R。因此,L指向新读取的字符的开头(在buf的开头),R指向结尾(最后读取的字符之后的一个字符)。

此后,逗号运算符有效地使L==R用作? :的控制值。如果未读取任何字符,则L等于R,并且例程尝试返回EOF(但如上所述可能会失败)。如果读取了字符,*L++返回第一个字符,并递增L指向下一个字符。

在随后的调用中,如果缓冲区中有字符,则&&L==R的左操作数为false,因此不评估右操作数。该表达式然后看起来像false && (DoesNotMatter) ? EOF : *L++。由于&&的结果为false,因此*L++返回缓冲区中的下一个字符并前进L。随着调用的继续,最终L将等于R,并且缓冲区将为空,从而导致读取新数据。

答案 3 :(得分:3)

This function is essentially a fancy getchar() that buffers the input。静态声明部分实际上只运行了一次。 让我们分解一下。 buf是缓冲区。 LR可能代表左右。因此,它们指向缓冲区的开始和结束。如果L == R为false,则缓冲区中仍然有内容,因此不评估第二个条件,我们从缓冲区中获取一个字符,并增加左指针。如果L == R为true,则对第二部分进行求值,该部分尝试使用指向起始点的L和指向R的{​​{1}}填充缓冲区,再加上读取的字符数来自L。逗号运算符表示将忽略第一部分,并且我们再次评估stdin,如果我们读取了一些字符,则为false,因此我们返回第一个字符,但是如果我们未读取任何内容,则为true,因此我们返回{ {1}}实际上并不能保证可以用char表示。返回类型应该为L == R

以下是更具可读性的版本(未经测试):

EOF