sprintf/snprintf是否会分配额外的内存?

11

我正在编写一个库,并希望使其完全不依赖于特定的资源,这也意味着该库应该与用户提供的内存分配函数一起工作。该库允许用户设置自己的错误处理函数,当出现错误消息时,将调用该函数并将该错误消息作为参数传递,例如:

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 系统手册对此保持沉默。


3
char* buf[BUF_SIZE]; 或者你的意思是 char buf[BUF_SIZE]; - Sourav Ghosh
1
就算它在内部使用malloc,只要它不造成内存泄漏,你为什么还要担心呢? - Blagovest Buyukliev
2
至少对于newlib库,格式化浮点数需要额外的内存。 - Kijewski
仅仅查看newlib源代码,似乎的确是这种情况,它默认使用40字节缓冲区从堆栈中分配,并在某些转换不足时分配更多空间。 - Antti Haapala -- Слава Україні
1
如果您只需要添加文件名和行号,可以在编译时创建字符串... - Valeri Atamaniouk
显示剩余9条评论
2个回答

14

根据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

非常感谢您展示了如何测试特定实现的方法。 - firegurafiku

11

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

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

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接