Не в состоянии понять, что делает эта функция C - PullRequest
4 голосов
/ 26 мая 2019

Я видел этот кусок кода несколько раз, но не могу понять, что он делает

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 всегда должно быть верным, верно? поскольку оба указателя указывают на одну и ту же переменную. Я не могу понять, что проверяет вторая часть условия. Может ли кто-нибудь помочь мне?

Ответы [ 4 ]

5 голосов
/ 26 мая 2019

Это сломанная функция, написанная кем-то, кто считал себя умным.

Объекты buf, L и R определены с static, они существуют в течение всего временивыполнения программы и инициализируются, когда начинается выполнение.Они сохраняют свои значения между вызовами функции.

Номинально функция возвращает либо символ (*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 && (DoesNotMatter) ? EOF : *L++.Поскольку результат && равен false, *L++ возвращает следующий символ в буфере и увеличивает L.Поскольку вызовы продолжаются, в конечном итоге L будет равняться R, и буфер будет пуст, что приведет к чтению новых данных.

5 голосов
/ 26 мая 2019

Это в основном эквивалентно:

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++;
}
5 голосов
/ 26 мая 2019

Все переменные объявлены как 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)

3 голосов
/ 26 мая 2019

Эта функция, по сути, необычная getchar(), которая буферизует входные данные . Часть статического объявления действительно запускается один раз. Давайте разберемся с этим. buf это буфер. L и R вероятно означают левый и правый. Таким образом, они указывают на начало и конец буфера. Если L == R равно false, у нас все еще есть что-то в буфере, поэтому второе условие не оценивается, и мы получаем символ из буфера и увеличиваем левый указатель. Если L == R истинно, вычисляется вторая часть, которая пытается заполнить буфер с L, указывающим на самое начало, и R, указывающим на L плюс количество символов, прочитанных с stdin. Оператор запятой означает, что первая часть игнорируется, и мы снова вычисляем L == R, что ложно, если мы читаем некоторые символы, поэтому мы возвращаем первый, но верно, если мы ничего не читали, поэтому мы возвращаем EOF который на самом деле не гарантируется быть представленным с помощью символа. Тип возвращаемого значения, вероятно, должен быть int.

Вот более читаемая версия (не проверено):

inline int nc() // notice the return type
{
    static char buf[100000];      // run only once
    static char *beg = buf; // L  // run only once
    static char *end = buf; // R  // run only once

    if (beg != end)  // If buffer is not empty
    {
      return *beg++; // return a character
    }
    beg = buf;
    // if the buffer is empty, try to read into it
    end = buf + fread(buf, 1, 100000, stdin);
    if (beg != end)
    {
      return *beg++;
    }
    // If reading failed, return EOF
    return EOF;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...