【问题标题】:Using fscanf with dynamically allocated buffer将 fscanf 与动态分配的缓冲区一起使用
【发布时间】:2010-03-27 14:32:29
【问题描述】:

我得到以下代码:

char buffer[2047];
int charsRead;

do {
    if(fscanf(file, "%2047[^\n]%n%*c", buffer, &charsRead) == 1) {
        // Do something
    }
} while (charsRead == 2047);

我想将此代码转换为使用动态分配的变量,以便在经常调用此代码时不会出现严重的内存泄漏。因此,我尝试了这个:

char *buffer = malloc(sizeof(char) * 2047);
int *charsRead = malloc(sizeof(int));

do {
    if(fscanf(file, "%2047[^\n]%n%*c", *buffer, charsRead) == 1) {
        // Do something
    }
} while (*charsRead == 2047);

很遗憾,这不起作用。我总是在 fscanf 调用的 if 语句之前收到“EXC_BAD_ACCESS”错误。 我做错了什么?

感谢您的帮助!

--瑞

【问题讨论】:

  • 仅供参考,根据定义,“sizeof(char)”始终为 1。

标签: c dynamic malloc scanf


【解决方案1】:

原始代码比新代码泄漏的可能性要小得多,因为编译器正在为您管理内存,但如果您觉得必须,请更改为:

if(fscanf(file, "%2047[^\n]%n%*c", buffer, charsRead) == 1) {

你不想在这里取消引用缓冲区,就像你在第一段代码中所做的那样。这样做会给你缓冲区中的第一个字符,但你需要缓冲区的地址。

【讨论】:

  • 抱歉,这可能是一个非常基本的问题,但是如果像 char buffer[2047]; 那样分配缓冲区,我可以释放()缓冲区吗?我知道如果变量超出范围就会泄露?
  • 你不会free()你没有动态分配的东西,所以没有。
  • @ryyst:如果它像你最初那样分配为一个数组,它就不会被泄露。但是,如果它是动态分配的,并且指针在您没有先释放的情况下超出范围,那么它将被泄漏。这就是为什么您通常尽可能避免动态分配的原因 - 如果您不小心,它会让您容易泄漏。
  • @ryyst 不,编译器在第一个示例中分配了缓冲区(在堆栈上),编译器将负责释放它。
  • @rryst 您似乎对内存管理和堆栈的工作方式感到非常困惑。您可能想了解一下。
【解决方案2】:

最好使缓冲区至少比您希望读取的字符数长一个字节,这样您也可以保存终止的 NUL。除此之外,原件不会泄漏;一旦包含代码的函数返回,这些变量就消失了。如果它有泄漏,那一定是你没有向我们展示的东西。

【讨论】:

  • 所以我的第一个版本中的缓冲区内存在超出范围时会被释放?我一直以为它会变成一个只占空间的“悬空”指针……
  • +1 是迄今为止唯一一个注意到缓冲区溢出错误的人。
【解决方案3】:

要回答“我做错了什么”,您将取消引用您的 buffer 指针。因为它被声明为char *,当你写*buffer时,你会得到缓冲区中的第一个字符,你想要整个东西,所以只需从前面删除*,如下所示:

if(fscanf(file, "%2047[^\n]%n%*c", buffer, charsRead) == 1) {

但是,你似乎有一个错误的前提。在您的第一个代码 sn-p 中使用静态分配的(在堆栈上)数组不会“泄漏内存”。恰恰相反,因为您的第二个代码 sn-p 中没有任何空闲,这就是会泄漏内存的代码。如果您在编译时知道数组需要的大小(在本例中为 2047),那么您应该(通常,总是有例外,但您还没有那么先进)使用静态数组而不是动态分配一个。

【讨论】:

  • +1 用于修复代码以及回答问题。
【解决方案4】:

我发现您的代码存在三个问题。首先,要回答您提出的问题,您应该将buffer 而不是*buffer 作为第三个参数传递给fscanf。 Neil 的回答很好地解释了原因。

其次,你有一个缓冲区溢出。 fscanf 自动将空终止字符附加到扫描的输入。您提供给fscanf 的缓冲区必须有足够的空间用于扫描的输入空终止字符。如果要扫描 2,047 个字符,则缓冲区需要 2,048 个字符长。

第三,您的代码的新版本是存在内存泄漏的。您以前的版本没有泄漏,因为那里的缓冲区是在堆栈上分配的(如果它是全局变量,则在静态存储中)。当函数返回时,缓冲区使用的堆栈空间将被回收。使用malloc 从堆中分配缓冲区意味着 负责在使用完缓冲区后随后调用free 来回收分配的堆内存。在我看来,您在堆栈上分配 buffer 的原始代码版本要好得多。

唯一可能更可取新版本的情况是,如果您的目标是堆栈空间非常有限的系统(如某些嵌入式系统的情况)。在这样的系统上,在堆栈上分配一个大缓冲区可能不是一个好主意。在这种情况下,最好使用malloc 从堆中分配缓冲区,以避免可能的堆栈溢出。如果是这种情况,那么您必须小心避免通过调用free 来释放内存来避免内存泄漏

【讨论】:

    【解决方案5】:

    动态分配 charsRead 并不能完成任何事情。回到您之前使用 charsRead 的方式,也许这会解决您的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      • 2021-07-21
      • 2015-01-12
      • 1970-01-01
      • 2021-07-31
      • 2020-02-26
      相关资源
      最近更新 更多