内存不足时如何输出一些内容?

我有一组自定义的 malloc[、realloc 和 calloc] 和 free 函数。目前,该项目仅将它们用作函数普通版本的包装器。这些包装器函数会终止程序并应提供输出以指示错误。

目前输出错误消息的函数如下所示:

#ifdef __linux__
void memory_error() {
    if (errno == ENOMEM)
        printf("%s\n", "Out of memory or memory is limited. Fatal error. The program needs to terminate.");
    else
        printf("%s\n", "Memory allocation failed. Fatal error. The program needs to terminate.");
}
#else
void memory_error() {
    printf("%s\n", "Memory allocation failed, likely due to being out of "
                   "memory or memory being limited for this program. "
                   "Fatal error. The program needs to terminate.");
}
#endif

问题是printf()有时会分配内存,如果内存不足,就会失败。会fwrite()分配内存吗?如果是这样,是否有一个可移植的(因此没有写入系统调用)选项来输出某些内容而无需分配更多内存?

到目前为止,作为 Unix 系统的有效解决方案,我可以这样做:

#ifdef __unix__
#define write_str_no_alloc(str) write(STDERR_FILENO, str, sizeof(str))
#else
#define write_str_no_alloc(str) ???
#endif

我缺少适用于 Windows 的解决方案。我更希望有一个适用于两者的通用解决方案,但这对我来说是可行的。

8

  • 1
    尝试puts改用。


    – 

  • @dbush 你确定这puts不会在任何主要系统上分配缓冲区吗?如果是这样,你能给我至少 linux 和 windows 的源代码吗?


    – 

  • 1
    我建议您写入stderr而不是stdout(即使用fprintf而不是printf,或者更确切地说fputs而不是puts),因为stderr通常不带缓冲。这将使尝试分配缓冲区的可能性降低。


    – 


  • 1
    @Tenobaal:我推荐使用stderr而不是stdout,我也推荐使用fputs而不是fprintf。我猜就分配内存而言,fwrite相当于。fputs


    – 


  • 1
    @Tenobaal:在大多数平台上,标准错误(stderr)是无缓冲的。但是,并不要求这样做。据我所知,并未stderr说明是否如此。


    – 



最佳答案
3

在 POSIX 系统上,该write函数直接调用write系统调用,因此不会发生内存分配。

Windows 有一个_write函数,它是 POSIX 兼容层的一部分,并且也有

这些函数直接调用操作系统来执行比流 I/O 提供的更低级别的操作。低级输入和输出调用不会缓冲或格式化数据。

因此你可以这样做:

#ifdef _MSC_VER
#include <io.h>
#define write_str_no_alloc(str) _write(2, str, sizeof(str))
#else
#define write_str_no_alloc(str) write(STDERR_FILENO, str, sizeof(str))
#endif

请注意,这要求参数是字符串文字或数组,否则sizeof不会给出所需的结果。

6

  • 它是否在程序的堆上分配内存?


    – 

  • 不太可能,但这可能是你能做的最好的事情了。


    – 

  • 我个人会用于WriteFileWindows。


    – 

  • 一旦我能够使用 Windows 机器或者其他人能帮我测试一下,我就会将此标记为已解决。遗憾的是,我认为 MS 文档中没有提到相关行为。尽管WriteFile提到了输出缓冲区。


    – 

  • MS 文档提到不使用缓冲区_write。所以我将采用这种方式。谢谢!


    – 

您可以编写自己的safe_printf()函数,该函数永远不会调用malloc()。来自我的问答:

这是一个永远不会调用safe_printf()的函数malloc()

我最终编写了一个safe_printf()从不调用的函数malloc(),以防有人需要它。我已经测试过了,它在里面运行良好malloc()。它使用write()系统调用(在 Linux 上可用,但在 std C 中不可用)将字符写入 stdout。

这里是:

#define SAFE_PRINTF_BUF_SIZE 2048
__attribute__((__format__(printf, 1, 2)))
int safe_printf(const char *format, ...)
{

只需将write()我的safe_print()函数替换为函数,它便可在 Windows 和 Linux 中运行。或者,使用 Windows 的,如

获取代码

用法与 完全相同printf()

示例(根据):

safe_printf("Failed to get a timestamp. errno = %i: %s\n",
    errno, strerror(errno));

3

  • 确定snvprintf不会分配任何内存吗?因为 @dbush 的答案已经足够了,并且每个使用的函数都记录为不分配内存。这里也是这种情况吗?


    – 

  • printf()mallocs,因为除了它分配的内存位置外,没有地方可以写入格式化的字符串。vsnprintf()将格式化的字符串写入您提供的内存位置。但是,C 标准可能无法保证这一点,并且它可能基于您正在使用的标准库实现。


    – 

  • 1
    Sraples 我也是这么想的。我想在为项目构建日志记录功能时会重新考虑这一点。目前使用就足够了write/_write。在最坏的情况下,我必须自己进行格式化或不允许格式化非分配日志记录。


    – 

如果最终选择是printf()(输出到stdout),请务必跟进fflush(stdout)以确保它可以输出。

特别是在错误情况下以及有关刷新的许多规则中,最好将此消息发送出去。

2

  • 我最后写了个换行符,这也会刷新输出缓冲区。更重要的是,printf 不满足要求。讽刺的是, 的存在暗示了这一点fflush


    – 

  • @Tenobaal “最后写一个换行符,这也会刷新输出缓冲区。”–>


    –