【问题标题】:Does sprintf/snprintf allocate additional memory?sprintf/snprintf 是否分配额外的内存?
【发布时间】:2015-02-17 20:05:51
【问题描述】:

我正在编写一个库,并希望使其绝对与资源无关,这也意味着该库应该与用户提供的内存分配函数一起使用。库还允许用户设置自己的错误处理函数,这些函数以错误消息作为参数调用,如下所示:

typedef void (*error_handler)(const char* msg);

库代码自己准备错误消息,有点像这样(省略消息格式化失败的情况):

char buf[BUF_SIZE];
snprintf(buf, BUF_SIZE, "Oops found at file '%s' line %d", __FILE__, __LINE__);

但是我可以确定 snprintf 不会为 malloc 的内部使用分配更多内存,显然会绕过用户提供的分配例程吗?我的 Linux 系统中的手册页对此保持沉默。

【问题讨论】:

  • char* buf[BUF_SIZE]; 还是您的意思是char buf[BUF_SIZE];
  • 我不知道如何避免它。
  • 即使它在内部使用malloc,只要它不泄漏内存,你为什么要担心?
  • 至少对于 newlib 仅用于格式化浮点数需要额外的内存。
  • 如果您只需要添加文件名和行,您可以在编译时制作字符串...

标签: c memory-management stdio


【解决方案1】:

根据glibc,newlibc源代码,它们在某些情况下都使用malloc,但不是在最可能的情况下。

如果您想知道当前代码何时执行malloc 或任何其他函数,您可以在 Linux 上像这样挂钩 libc 函数:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void *malloc(size_t size) {
    static void *(*original_malloc)(size_t) = NULL;
    if (! original_malloc) {
        /* get the pointer to the original malloc */
        original_malloc = dlsym(RTLD_NEXT, "malloc");
    }

    void *rv = original_malloc(size);
    printf("allocated %zd bytes of memory\n", size);
    return rv;
}

将其编译成共享对象

gcc -Wl,--no-as-needed -shared -ldl -fPIC hook.c -o hook.so

并给出代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    char buf[256];
    char *malloced;

    printf("about to run sprintf\n");
    sprintf(buf, "%.250f", 0.123456789);
    printf("done\n");

    printf("about to run asprintf\n");
    asprintf(&malloced, "foo");
    free(malloced);
    printf("done\n");
}

编译成prog,可以运行:

% LD_PRELOAD=./hook.so ./prog
about to run sprintf
done
about to run asprintf
allocated 100 bytes of memory
allocated 4 bytes of memory
done

【讨论】:

  • 非常感谢您展示了如何测试特定实现的方法。
【解决方案2】:

像任何库例程一样,sprintfsnprintf可能会也可能不会分配内存供内部使用。

他们不会为结果字符串分配内存。该内存必须由调用者以某种方式分配,并且其地址作为第一个参数传递。如果分配的内存不够大,那么sprintf 将有未定义的行为(因为无法告诉它有多少可用空间),snprintf 将截断结果(假设 size 参数是准确的)。

如果sprintfsnprintf 的实现分配了内存并且没有安排释放它,那将是内存泄漏。这样的泄漏实际上不会违反语言标准(关于资源分配几乎没有什么可说的),但它会被认为是该实现中的一个错误。

特别是,如果您自己的代码使用自己的内存分配器而不是malloc,那么您调用的任何库函数都可以在内部调用malloc,除非您使用某些系统特定的功能来调用malloc,甚至在标准库中,改为调用您的分配器。例如,fopen() 特别有可能为缓冲区分配内存。

如果您将标准库对malloc 的调用替换为对您自己的分配器的调用,则需要确保您还替换了对realloccallocfree 的任何调用,可能还有一个或多个系统特定的例程。例如,程序完成时运行的清理代码将关闭打开的文件,这可能涉及到对free 的调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-17
    • 2011-07-11
    • 2011-04-16
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    相关资源
    最近更新 更多