【问题标题】:Functions responsibility on data in CC中数据的功能责任
【发布时间】:2015-10-26 21:00:45
【问题描述】:

最近我在工作中遇到了一个问题,你有两个功能;一个人打开一个文件描述符(它是函数中的一个局部变量),并将它传递给另一个用于读取或写入的函数。现在,当其中一个操作读/写失败时,正在执行读/写的函数关闭此文件描述符,并返回。 问题是,谁的责任是关闭文件描述符,或者说做清理:

  1. 创建 fd 的函数

  2. 读/写时遇到错误的函数

这类情况有设计规则吗?让我们说创建和清理。

顺便说一句,问题是两个函数都试图关闭 fd,导致第二次调用 close 时崩溃。

【问题讨论】:

  • 只需将其写入在任何给定时间拥有文件句柄所有权的那些函数的合同中。 PS:你用的是C还是C++?
  • 如果你必须fd从一个函数传递到另一个函数并且任何一个都可以关闭它,只需将其设置为NULL。并且不要忘记在使用fclose 之前先进行测试。但最简单的方法可能是避免该问题 - 如果发生错误,为什么要在函数内关闭文件?即使您喜欢这样,如果它确实出错了,为什么不将它传达给调用者呢?
  • @Jongware:文件描述符是小(ish)整数;设置为 NULL 是不合适的,但 -1 可能是明智的。 OTOH,文件描述符很少作为指向int 的指针传递,因此更改很少会反映在调用代码中。
  • 一般情况下,打开文件的函数应该关闭它;遇到错误的函数应该报告错误,但不关闭文件。但是,只要合同被记录和执行,您就可以按照自己喜欢的方式工作——调用代码需要知道被调用代码何时关闭文件以避免双重关闭。您确定您使用的是文件描述符而不是FILE *(文件流)吗?两次关闭文件描述符不太可能导致崩溃(错误,是的,但不是崩溃)。 OTOH,在已关闭的文件流上调用 fclose() 可能会导致问题。
  • @Jongware:将close() 与错误(已关闭或永远无效)的文件描述符一起使用会生成-1 作为返回值,并且通常将errno 设置为EBADF。没有崩溃的危险,因为不涉及指针。即使使用文件流,您也很少将FILE ** 传递给函数,因此被调用的函数仍然无法影响调用函数中的FILE * 值。正如我在另一条评论中概述的那样,如果被调用的函数关闭文件,代码必须小心——如果不关闭文件通常是最好的,但只要每个人都知道发生了什么,这不是绝对必要的。

标签: c++ c


【解决方案1】:

这个答案分为两部分——一般设计问题和适合您情况的详细机制。

一般设计

正确处理文件描述符等资源并确保它们被正确释放是一个重要的设计问题。有多种方法可以管理有效的问题。还有一些没有。

您的标签使用 C 和 C++;请注意,C++ 有额外的可用机制。

在 C++ 中,RAII — Resource Acquisition Is Initialization — 习语很有帮助。获取资源时,请确保获取资源的任何对象都会初始化一个值,该值将被正确销毁并在销毁时释放资源。

在这两种语言中,通常最好是负责分配资源的函数也释放它。如果一个函数打开一个文件,它应该关闭它。如果给函数一个打开的文件,它不应该关闭该文件。

在 cmets 中,我写道:

通常,打开文件的函数应该关闭它;遇到错误的函数应该报告错误,但不关闭文件。但是,只要合同被记录和执行,您就可以按照自己喜欢的方式工作——调用代码需要知道被调用代码何时关闭文件以避免双重关闭。

对于被调用函数来说,有时关闭文件(发生错误)通常是一个糟糕的设计,但其他时候则关闭文件(没有错误)。如果您必须这样做,那么通知调用函数文件已关闭是至关重要的;被调用的函数必须返回一个错误指示,告诉调用代码该文件不再有效,不应使用也不应关闭。只要信息被传递和处理,就没有问题——但功能更难使用。

请注意,如果一个函数被设计为返回一个打开的资源(它是一个负责打开文件并使其对调用它的函数可用的函数),那么关闭文件的责任就落在了调用打开函数。这是一个合法的设计;您只需要确保有一个知道如何关闭它的函数,并且调用代码确实会关闭它。

类似的 cmets 也适用于内存分配。如果一个函数分配内存,你必须知道什么时候释放内存,并确保它被释放。如果它是为当前函数和它调用的函数而分配的,则应该在返回之前释放内存。如果它被分配给调用函数使用,那么释放的责任就会转移到调用函数。

详细机制

您确定您使用的是文件描述符而不是FILE *(文件流)吗?两次关闭文件描述符不太可能导致崩溃(错误,是的,但不是崩溃)。 OTOH,在已关闭的文件流上调用 fclose() 可能会导致问题。

一般来说,在 C 语言中,您通过值传递文件描述符,它是一个小整数,因此没有办法告诉调用函数文件描述符不再有效。在 C++ 中,您可以通过引用传递它们,尽管这样做并不传统。与FILE * 类似;它们通常通过值而不是引用传递,因此无法通过修改传递给函数的值来告诉调用代码文件不再可用。

您可以通过将文件描述符设置为-1 来使其无效;那永远不是有效的文件描述符。使用0 是个坏主意;它相当于使用标准输入。您可以通过将文件流设置为0(又名NULL)来使其无效。将空指针传递给尝试使用文件流的函数往往会导致崩溃。传递一个无效的文件描述符通常不会导致崩溃——调用可能会失败并在errno 中设置EBADF,但这通常是损坏的极限。

使用文件描述符,您很少会遇到崩溃,因为文件描述符不再有效。使用文件流,如果尝试使用无效的文件流指针,各种事情都会出错。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    • 2018-06-21
    相关资源
    最近更新 更多