Понимание реализаций функций встроенной библиотеки C - PullRequest
18 голосов
/ 11 июля 2011

Итак, я проходил второе издание K & R, выполняя упражнения.Чувствуя себя довольно уверенно, выполнив несколько упражнений, я решил проверить фактическую реализацию этих функций.Именно тогда моя уверенность скрылась со сцены.Я не мог понять ничего из этого.

Например, я проверяю getchar():

Вот прототип в libio/stdio.h

extern int getchar (void);

Так что я следую этомучерез это и получает это:

__STDIO_INLINE int
getchar (void)
{
  return _IO_getc (stdin);
}

Снова я следую за этим к libio/getc.c:

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

И я перенесен в другой заголовочный файл libio/libio.h, который довольноcryptic:

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

На этом я наконец и закончил свое путешествие.

Мой вопрос довольно широкий.Что все это значит?Я не мог на всю жизнь понять что-либо логичное из этого, посмотрев на код.Выглядит как куча кодов, абстрагированных от слоев за слоем.

Более важно, когда он действительно получает символ из stdin

Ответы [ 5 ]

24 голосов
/ 11 июля 2011

_IO_getc_unlocked - это встроенный макрос.Идея состоит в том, что вы можете получить символ из потока, не вызывая функцию, что делает его достаточно быстрым для использования в тесных циклах и т. Д.

Давайте разберем его по одному слою за раз.Во-первых, что такое _IO_BE?

/usr/include/libio.h:# define _IO_BE(expr, res) __builtin_expect ((expr), res)

_IO_BE является подсказкой для компилятора, что expr будет обычно оценивать как res.Он используется для структурирования потока кода, чтобы быть быстрее, когда ожидание истинно, но не имеет другого семантического эффекта.Таким образом, мы можем избавиться от этого, оставив нам:

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

Давайте превратим это в встроенную функцию для ясности:

inline int _IO_getc_unlocked(FILE *fp) {
  if (_fp->_IO_read_ptr >= _fp->_IO_read_end)
    return __uflow(_fp);
  else
    return *(unsigned char *)(_fp->_IO_read_ptr++);
}

Короче, у нас есть указатель на буфери указатель на конец буфера.Мы проверяем, находится ли указатель вне буфера;если нет, мы увеличиваем его и возвращаем тот символ, который был при старом значении.В противном случае мы вызываем __uflow для повторного заполнения буфера и возврата вновь прочитанного символа.

Таким образом, это позволяет нам избежать накладных расходов при вызове функции до тех пор, пока нам фактически не потребуется IO для повторного заполнения входного буфера.

Имейте в виду, что стандартные библиотечные функции могут быть такими сложными;они также могут использовать расширения языка C (такие как __builtin_expect), которые НЕ являются стандартными и могут НЕ работать на всех компиляторах.Они делают это потому, что должны быть быстрыми, и потому что они могут делать предположения о том, какой компилятор они используют.Вообще говоря, ваш собственный код не должен использовать такие расширения без крайней необходимости, так как это затруднит перенос на другие платформы.

4 голосов
/ 12 июля 2011

Переходя от псевдокода к реальному коду, мы можем его разбить:

if (there is a character in the buffer)
  return (that character)
else
   call a function to refill the buffer and return the first character
end

Давайте используем оператор?: :

#define getc(f) (is_there_buffered_stuff(f) ? *pointer++ : refill())

Немногоближе:

#define getc(f) (is_there_buffered_stuff(f) ? *f->pointer++ : refill(f))

Теперь мы почти у цели.Чтобы определить, есть ли уже что-то буферизованное, он использует указатель структуры файла и указатель чтения в буфере

 _fp->_IO_read_ptr >= _fp->_IO_read_end ?

Это фактически проверяет условие, противоположное моему псевдокоду, "буфер пуст",и если это так, он вызывает __uflow(_fp) // "underflow", в противном случае он просто попадает прямо в буфер с указателем, получает символ и затем увеличивает указатель:

? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++)
2 голосов
/ 12 июля 2011

Я очень рекомендую Стандартная библиотека C П. Дж. Плаугера. Он дает представление о стандарте и обеспечивает реализацию каждой функции. Реализация проще, чем то, что вы увидите в glibc или современном компиляторе C, но все же использует макросы, такие как _IO_getc_unlocked(), который вы опубликовали.

Макрос будет извлекать символ из буферизованных данных (который может быть буфером ungetc) или считывать его из потока (который может считывать и буферизовать несколько байтов).

1 голос
/ 11 июля 2011

Причина, по которой существует стандартная библиотека, заключается в том, что вам не нужно знать точные детали имплантации этих функций. Код, который реализует библиотечные вызовы в какой-то момент, должен использовать нестандартные системные вызовы, которые должны иметь дело с проблемами, которые могут вас не беспокоить. Если вы изучаете C, убедитесь, что вы понимаете другие программы на C, кроме stdlib, как только вы немного углубитесь в stdlib, но это все равно не будет иметь большого смысла, пока вы не поймете системные вызовы.

0 голосов
/ 11 июля 2011

Определение getchar () переопределяет запрос как конкретный запрос для символа из stdin.

Определение _IO_getc () выполняет проверку работоспособности, чтобы убедиться, что FILE * существует и не является End-Of-File, а затем блокирует поток, чтобы другие потоки не повредили вызов _IO_getc_unlocked ().

Определение макроса _IO_getc_unlocked () просто проверяет, находится ли указатель чтения в конце или за концом файловой точки, и либо вызывает __uflow, если оно есть, либо возвращает символ в указателе чтения, если это не так.

Это стандартный материал для всех реализаций stdlib. Вы не должны когда-либо смотреть на это. Фактически, многие реализации stdlib будут использовать язык ассемблера для оптимальной обработки, что еще более загадочно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...