C 中的 fread 实际上是如何工作的?

How does fread in C actually work?

我了解到 fread() 具有以下函数定义:

size_t fread(void *buffer, size_t size, size_t qty, FILE *inptr);

我也了解到inptr是一个文件指针,当使用fopen()函数打开一个FILE指针时返回的。我的问题是 inptr 是否将文件的每个 character/letter 的内存地址存储在其内存中?如果是这种情况,inptr 中的内存地址是否被复制到 *buffer(指向缓冲区数组的指针)?

还有一件事让我很困惑。每次 fread() 被调用时,size * qty 字节的内存被 copied/transferred。是inptr本身指向的文件内容还是被copied/transferred的文件内容的内存地址?

如果有人能帮助我消除困惑,我将不胜感激。谢谢:)

FILE 由您的操作系统实现。在 FILE 上运行的功能由您的系统实现。你不知道。要知道,您需要浏览操作系统的源代码。
inptr 可能是指向操作系统分配的内存的指针。或者它可能是一个数字,您的操作系统使用它来查找它的数据。无论如何,它是一个句柄,您的系统使用它来查找 FILE 特定数据。您的系统决定该数据中的内容。出于缓存目的,可能所有字母都缓存在某个缓冲区中。也许不是。
fread 打电话。 Freadinptr 句柄后面的底层实体读取数据。 inptr 由您的系统解释,以访问底层内存或结构或设备或硬盘驱动器或打印机或键盘或鼠标或任何东西。它读取 qty*size 字节的数据。这些数据放在 buffer 中。那里没有指针。从设备读取的字节放置在 buffer.

指向的内存中

你的问题有点令人困惑(这可能是你问他们的原因)所以我会尽力回答。

FILE *inptr 是打开文件的句柄。你不要直接读它,它只是用来告诉相关函数操作什么。你可以把它想象成一个人在读取文件夹中的文件名,其中文件名用于标识文件,但内容是通过另一种方式访问​​的。

至于数据,它是从使用 fopen() 打开的文件中读取的,随后提供了一个文件句柄。数据与 FILE 指针没有直接关联,通常你不应该直接弄乱 FILE 指针(不要试图直接从它 read/write)。

我尽量不要对操作太技术化,因为您似乎是 C 的新手,但只是将 FILE * 视为计算机对 "naming" 文件的方式内部供自己使用,数据缓冲区只是内容。

你可以认为 fread 是这样实现的:

size_t fread(char *ptr, size_t size, size_t nitems, FILE *fp)
{
    size_t i;
    for(i = 0; i < size * nitems; i++) {
        int c = getc(fp);
        if(c == EOF) break;
        *ptr++ = c;
}

(我省略了 return 值,因为在我的简化插图中没有很好的方式来显示它。)

也就是说,fread读取了一堆字符,就好像是重复调用getc()一样。很明显,这引出了 getc 如何运作的问题。

您必须知道的是 FILE * 指向一个结构,该结构以某种方式包含读入内存的部分(不一定是全部)文件字符的缓冲区。所以,在伪代码中,getc() 看起来像这样:

int getc(FILE *fp)
{
    if(fp->buffer is empty) {
        fill fp->buffer by reading more characters from underlying file;
        if(that resulted in end-of-file)
            return EOF;
    }

    return(next character from fp->buffer);
}

问题的答案,

"how does fread() work?"

基本上是

"it asks your operating system to read the file for you."

操作系统内核的唯一目的或多或少是代表您执行此类操作。内核托管磁盘和文件系统的设备驱动程序,并且无论文件存储在什么地方(例如 FAT32 格式的硬盘、网络共享等),都能够为您的程序获取数据。

fread() 要求您的操作系统从文件中获取数据的方式在 OS 和 CPU 之间略有不同。回到 MS-DOS 的美好时光,fread() 函数会将各种参数(根据您的程序提供给 fread() 的参数计算)加载到 CPU 寄存器中,然后提高一个中断。中断处理程序实际上是 MS-DOS 的一部分,然后会去获取请求的数据,并将其放在内存中的给定位置。要加载的寄存器和要引发的中断都由 MS-DOS 手册指定。您传递给 fread() 的参数是系统调用所需参数的抽象。

这就是所谓的系统调用。每个操作系统都有一个系统调用接口。 Linux 上的 glibc 等库提供方便的函数,例如 fread()(它是标准 C 库的一部分),并为您进行系统调用(这在操作系统之间没有标准化)。

请注意,这意味着 glibc 不是操作系统的基本组成部分。它只是一个例程库,围绕 Linux 提供的系统调用实现了 C 标准库。这意味着您可以使用替代 C 库。例如,Android 不使用 glibc,即使它有一个 Linux 内核。

与 Windows 类似。 Windows 中的所有软件(C、C++、.NET 运行 时代等)都是使用 WIN32 API 库 (win32.dll) 编写的。 Windows 上的区别是NT 内核系统调用接口没有发布;我们不知道它是什么。

这导致了一些有趣的事情。

  • Linux 上的 WINE 重新创建 WIN32.dll,而不是 NT 内核系统调用接口。
  • Windows Windows 10 上 Linux 的子系统确实重新创建了 Linux 系统调用接口(这是可能的,因为它是 public 知识)。
  • Solaris、QNX 和 FreeBSD 玩同样的把戏。
  • 更奇怪的是,看起来 MS 已经为 Linux 做了一个 NT 内核系统接口垫片(即 WINE 没有做的事情)以允许 MS-SQLServer 运行 Linux。这实际上是 Windows 的 Linux 子系统。他们没有放弃这个。