【问题标题】:Fast way to check if file pointer points to a valid file检查文件指针是否指向有效文件的快速方法
【发布时间】:2024-04-24 16:20:02
【问题描述】:

我正在寻找快速(针对性能关键代码)、安全且跨平台的方法来检查 FILE* 是否实际上在之前成功调用 fopen() 时指向文件。

使用ftell() 询问当前位置是一种方法,但是 我怀疑它是否最快、准确、安全,或者没有比这种方式更直接、更专注的方法了。

【问题讨论】:

  • if (file_pointer == NULL) 是标准测试。发布您试图澄清问题的内容。
  • 一般来说,没有可移植的方法来检查指针是否有效。不限于文件指针。
  • @chux 检查指针是否为NULL,但它可能很好地指向某些东西。想象一下它是一个void* ,可以根据用户表示为字符串文字或FILE* 。你如何检查它是否是一个有效的FILE* ?感谢您的反对。
  • 可以使用if ((void *)some_object_pointer_from_somewhere == (void *) pointer_returned_from_previous_fopen_call)
  • @JuanPabloJuliosEstebaFiores 否决票是因为这个问题毫无意义。 FILE * 指针不存在于真空中,它们存在于编写得好或不好的程序中。在编写良好的程序中,任何FILE * 变量都包含(a)它的程序员初始化的NULL 值或(b)检查过的fopen 调用的返回值,这意味着当我们到这里我们知道它是非空的。但是不,对于随机的FILE * 指针,无法知道它是否有效。

标签: c file file-io stdio


【解决方案1】:

如果对fopen 的调用成功,但您想知道是否刚刚打开了文件或其他内容,我知道两种通用方法:

  1. 在文件描述符上使用fstat(或在您刚刚打开的同一路径名上使用stat),然后检查模式位。

  2. 尝试查找文件描述符。如果这按预期工作,它可能是一个文件;如果不是,那就是管道或套接字或类似的东西。

(1) 的代码可能看起来像

struct stat st;
fstat(fileno(fp), &st);
if(st.st_mode & S_IFMT) == S_IFREG)
    /* it's a regular file */

要执行 (2),我通常会寻求偏移 1,然后测试以查看我所处的偏移量。如果我在 1,它是一个可搜索的文件,我在程序的其余部分倒回 0。但如果我仍然是 0,它就不是一个可搜索的文件。 (当然,我会在打开文件后立即执行此操作,并将结果记录在与打开文件关联的我自己的标志中,因此对性能的影响很小。)

【讨论】:

    【解决方案2】:

    在C语言中有3种指针值:

    1. NULL 值,因为程序员初始化了它们(或者因为它们利用了默认的静态初始化)。
    2. 由指针返回函数返回的值,例如 fopenmalloc(尚未传递给 fclosefree)。
    3. 1 和 2 都不为真的值。

    简单的事实是,如果你有一个类型 3 的指针,语言中没有机制可以告诉你指针是否有效。如果你有一个指针 p 可能已经从 malloc 获得,但你不记得了,没有办法让编译器或运行时系统告诉你它当前是否指向有效内存.如果你有一个 FILE 指针 fp 可能已经从 fopen 获得,但你不记得了,没有办法让编译器或运行时系统告诉你它当前是否“指向" 一个有效的文件。

    因此,由程序员负责跟踪指针值,并使用有助于确定指针值是否有效的编程实践。

    这些方法包括:

    1. 始终将指针变量初始化为 NULL,或指向有效的东西。
    2. 当您调用返回指针的函数时,例如fopenmalloc,请始终测试返回值是否为NULL,如果是,请提前返回或打印错误消息或任何适当的内容.
    3. 当您使用完动态分配的指针并通过调用 fclosefree 或等效方法释放它时,请始终将其设置回 NULL。

    如果你虔诚地做这三件事,那么你可以通过做来测试指针是否有效

    if(p != NULL)
    

    if(p)
    

    同样,如果你虔诚地做这些事情,你可以通过做来测试指针是否无效

    if(p == NULL)
    

    if(!p)
    

    但只有在您认真执行步骤 1 和 3 时,这些测试才可靠地工作。如果你没有,那么有可能——而且很有可能——各种指针值是非 NULL 但无效的。


    以上是一种策略。我应该指出,步骤 1 和 3 并不是绝对必要的。另一种策略是虔诚地应用第 2 步,并且永远不要保留——永远不要尝试使用——可能为空的指针。如果像 fopenmalloc 这样的函数返回 NULL,你要么立即退出程序,要么立即从你所在的任何函数返回,通常带有一个失败代码,告诉你的调用者你不能做你的工作(因为你无法打开您需要的文件,或者您无法分配所需的内存)。在虔诚地应用规则 2 的程序中,您甚至不需要测试指针的有效性,因为此类程序中的所有指针值都是有效的。 (好吧,只要忠实地应用规则 2,所有指针都是有效的。如果您甚至忘记应用规则 2 一次,事情就会开始崩溃。)

    【讨论】:

    • +1 不过,我已经知道这些了。问题是我允许用户向这个void* 添加任何类型的数据,我让它检测它是否是一个以@ 开头的只读字符串,如果是,它会打开一个基于它的文件名称并将 FILE* fp 分配给此 void*。重要的是它在内存和性能方面进行了高度优化,因此我不想专门为文件预留另一个 void*。这也破坏了我尽可能少的函数参数的想法
    • 很遗憾,这里的人们认为这个问题“毫无意义”,当我在 ftell() 中放置一个指向其他内容的 void* 时,它会返回 -1 作为错误。如果它是由 ftell 不能容忍的标准定义的,那么当然可以,这是未定义的行为,但没有人真正从标准或手册页中引用这一点。
    • @JuanPabloJuliosEstebaFiores 通常,C 中的任何函数都不能容忍被传递给无效指针 - 因为一般来说,它无法优雅地处理它,因为它无法确定它是否有效或不是,除了尝试使用它,就好像它是有效的一样——如果它是无效的,那么它会以任意方式失败(即未定义的行为)。
    • @JuanPabloJuliosEstebaFiores 我怀疑你的性能需求是如此极端,以至于你真的需要求助于这种定义不明确的黑客,即使性能确实很重要,也可能有一种方法可以做到这一点(无论如何是——我承认我仍然不清楚)这既干净又高效,但这些是另一天的问题。
    • 我不会使用术语“无效指针”,因为指针始终是指向有效内存区域的指针。如果用户传递了无效指针,根据一般软件策略,这是他的错。如果我传递一个不是只读内存区域的指针,它也会出现段错误。