理解C内置库函数实现

sai*_*int 18 c

所以我正在通过K&R第二版做练习.做了一些练习后感觉非常自信我以为我会检查这些功能的实际实现.然后我的信心逃离现场.我无法理解任何一个.

例如,我检查getchar():

这是原型 libio/stdio.h

extern int getchar (void);
Run Code Online (Sandbox Code Playgroud)

所以我通过它来完成并得到这个:

__STDIO_INLINE int
getchar (void)
{
  return _IO_getc (stdin);
}
Run Code Online (Sandbox Code Playgroud)

我再次按照它来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;
}
Run Code Online (Sandbox Code Playgroud)

我被带到另一个头文件libio/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++)
Run Code Online (Sandbox Code Playgroud)

这是我最终结束旅程的地方.

我的问题很广泛.这是什么意思呢?通过查看代码,我无法为我的生活找出任何合乎逻辑的东西.看起来像一堆代码逐层抽象出来.

更重要的是,什么时候真正得到了角色 stdin

bdo*_*lan 24

_IO_getc_unlocked是一个无法解决的宏.这个想法是你可以从流中获取一个字符,而不必调用一个函数,希望它足够快,可以用于紧密循环等.

让我们一次分开一层.首先,是什么_IO_BE

/usr/include/libio.h:# define _IO_BE(expr, res) __builtin_expect ((expr), res)
Run Code Online (Sandbox Code Playgroud)

_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++) )
Run Code Online (Sandbox Code Playgroud)

为清晰起见,我们将其转换为内联函数:

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++);
}
Run Code Online (Sandbox Code Playgroud)

简而言之,我们有一个指向缓冲区的指针,以及指向缓冲区末尾的指针.我们检查指针是否在缓冲区之外; 如果没有,我们递增它并返回旧值的任何字符.否则我们调用__uflow重新填充缓冲区并返回新读取的字符.

因此,这允许我们在实际需要执行IO来重新填充输入缓冲区之前避免函数调用的开销.

请记住,标准库函数可能像这样复杂; 他们还可以使用__builtin_expect非标准的C语言扩展(例如),并且可能不适用于所有编译器.他们这样做是因为他们需要快速,因为他们可以对他们正在使用的编译器做出假设.一般来说,除非绝对必要,否则您自己的代码不应使用此类扩展,因为它会使移植到其他平台更加困难.