sprintf/snprintf 是否分配额外的内存?
Does sprintf/snprintf allocate additional memory?
我正在编写一个库并希望使其完全与资源无关,这也意味着该库应该与用户提供的内存分配函数一起使用。库也允许用户设置自己的错误处理函数,调用时将错误消息作为参数,如下所示:
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 系统中的手册页对此保持沉默。
与任何库例程一样,sprintf
和 snprintf
可能会也可能不会 分配内存供内部使用。
他们不会为结果字符串分配内存。该内存必须由调用者以某种方式分配,并将其地址作为第一个参数传递。如果分配的内存不够大,那么 sprintf
将有未定义的行为(因为无法告诉它有多少 space 可用),并且 snprintf
将截断结果(假设大小参数是准确的)。
如果 sprintf
或 snprintf
的实现分配了内存但没有安排释放它,那就是内存泄漏。这样的泄漏实际上不会违反语言标准(对于资源分配几乎没有说明),但它会被视为该实现中的错误。
特别是,如果您自己的代码使用自己的内存分配器而不是 malloc
,您调用的任何库函数都可以在内部调用 malloc
,除非您使用某些系统特定的功能来引发调用到 malloc
,即使在标准库中,也可以调用您的分配器。例如fopen()
,特别有可能为缓冲区分配内存。
如果您采取措施将标准库对 malloc
的调用替换为对您自己的分配器的调用,则需要确保您还替换了对 realloc
、calloc
,和 free
,可能还有一个或多个特定于系统的例程。例如,在程序完成时运行的清理代码将关闭打开的文件,这可能涉及对 free
.
的调用
根据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
我正在编写一个库并希望使其完全与资源无关,这也意味着该库应该与用户提供的内存分配函数一起使用。库也允许用户设置自己的错误处理函数,调用时将错误消息作为参数,如下所示:
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 系统中的手册页对此保持沉默。
与任何库例程一样,sprintf
和 snprintf
可能会也可能不会 分配内存供内部使用。
他们不会为结果字符串分配内存。该内存必须由调用者以某种方式分配,并将其地址作为第一个参数传递。如果分配的内存不够大,那么 sprintf
将有未定义的行为(因为无法告诉它有多少 space 可用),并且 snprintf
将截断结果(假设大小参数是准确的)。
如果 sprintf
或 snprintf
的实现分配了内存但没有安排释放它,那就是内存泄漏。这样的泄漏实际上不会违反语言标准(对于资源分配几乎没有说明),但它会被视为该实现中的错误。
特别是,如果您自己的代码使用自己的内存分配器而不是 malloc
,您调用的任何库函数都可以在内部调用 malloc
,除非您使用某些系统特定的功能来引发调用到 malloc
,即使在标准库中,也可以调用您的分配器。例如fopen()
,特别有可能为缓冲区分配内存。
如果您采取措施将标准库对 malloc
的调用替换为对您自己的分配器的调用,则需要确保您还替换了对 realloc
、calloc
,和 free
,可能还有一个或多个特定于系统的例程。例如,在程序完成时运行的清理代码将关闭打开的文件,这可能涉及对 free
.
根据glibc、newlibc的源代码,它们在某些情况下都使用malloc
,但在最有可能的情况下不会。
如果您想找出当前代码何时执行 malloc
或任何其他函数,您可以像这样在 Linux:
#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