Cpp 检查显示缓冲区“tmpf”在其旧内容被使用之前被写入

Posted

技术标签:

【中文标题】Cpp 检查显示缓冲区“tmpf”在其旧内容被使用之前被写入【英文标题】:Cpp check show Buffer 'tmpf' is being written before its old content has been used 【发布时间】:2015-10-01 07:12:50 【问题描述】:

这个函数在 cpp 检查时给了我一个警告(在第 5 行)。我不知道这背后的原因,也不知道这是否值得修复。

static char *get_file_name(const char *fmt, long n, int has_fmt_spec)

    static char tmpf[4096];
    int bytes;
    bytes = has_fmt_spec ? snprintf(tmpf, 4095, fmt, n) : 
        snprintf(tmpf, 4095, "%s%ld", fmt, n);<--- Buffer 'tmpf' is being written before its old content has been used.
    if (bytes >= 4095) 
        printf("file name too long\n");
        exit(EXIT_FAILURE);
    
    return tmpf;

【问题讨论】:

我建议您要么使用三元表达式来选择格式(而不是使用两个本质上相等的函数调用),要么使用if 语句。或者,如果has_fmt_spec 为零,您实际上可以将格式分配fmt 谢谢你的回答......我会试试这个......但只是出于好奇,代码有什么根本错误吗? 不,代码没有任何“错误”,行为是明确定义的。 风格上这是另一个问题... ;) 我喜欢三元运算符并一直使用它们。但它们的主要好处是使代码更简洁易读。如果您发现该行变得冗长且令人困惑并且调用多个函数并且调用或结果的顺序有问题,那么最好(更清晰)只使用 if/else。 【参考方案1】:

一些反馈:

    ᴅᴏɴᴅᴏɴᴛᴛᴀᴅᴅʀᴇꜱꜱᴀᴅᴅʀᴇꜱꜱᴏꜰʙᴜꜰꜰᴇʀꜰʀᴏᴍᴀ:避免将指针返回到函数中声明的静态缓冲区。它将范围层次结构颠倒过来,并且它是不可重入的(例如,不是线程安全的)。这很容易出错,因为该函数会覆盖其返回指针所指向的缓冲区,该缓冲区可能由另一个线程(甚至同一个线程)持有或使用。人们很容易忘记内存是易失性的,从而导致一些非常隐蔽的问题,即使不是立即诊断,也很难在代码的持续开发或扩展中的某个时间点诊断出来。

    ʀᴇᴛᴜʀɴᴏᴩᴛɪᴍᴀʟꜱɪᴢᴇʜᴇᴀᴩᴩᴏɪɴᴛᴇʀꜰᴜɴᴄᴛɪᴏɴꜰᴜɴᴄᴛɪᴏɴ:更常见的是从堆中分配内存并返回指针,让来电释放它。在这种情况下,缓冲区大小不需要固定为任意大小,因为snprintf() 允许您计算存储结果所需的长度;这样您就可以分配正确的内存量,从而避免检查缓冲区溢出的需要。理想情况下,结果缓冲区大小应仅受所提供参数的大小和可用内存的限制。该示例显示了从函数传播堆指针的两种方法。一种方式将其作为函数的返回值返回;另一种方式是通过函数参数传递它。

    ᴀᴠᴏɪᴅ ᴛᴇʀᴍɪɴᴀᴛɪɴɢ ᴩʀᴏɢʀᴀᴍ ɪɴ ᴀ ꜰᴜɴᴄᴛɪᴏɴ: 从函数内部退出程序通常是个坏主意(尤其是退出函数)。如果错误是不可恢复的并且退出对于应用程序的性质是可以接受的(对于像烤面包机这样的嵌入式系统,则最好对错误处理采取策略性,将潜在的致命错误传播到 main() 必要时从那里退出)烤箱或控制塔雷达,它不是)。客户讨厌应用程序意外退出,这可能会产生严重影响。不要让它发生在任意函数中的一个不可预见的小错误。

    ᴇʀʀᴏʀ ʜᴀɴᴅʟɪɴɢ ɪɴ ꜰᴜɴᴄᴛɪᴏɴ 下面示例中的错误处理是足够的,但很少。在 main() 级别,示例代码假定函数中的任何错误只能是“内存不足”。处理它的另一种方法可能是返回多个全局错误特定常量之一(通过枚举,#defineconst),或通过另一个 char * 参数返回错误消息,或记录错误细节从函数内部传递一个通用的失败代码,然后让调用者处理条件(如果无法处理,它也会向上传递)。注意:示例中使用的atoi() 可能会失败(即,如果它无法解析给定的字符串),但如果是,则返回 0,因此对于此示例来说已经足够了。重点是 - 始终检查返回值和/或制定周密的错误处理策略。很多错误是由于看似无害的函数的意外故障而发生的,如果返回值没有被检查/处理,就很难追踪。

    ᴄᴏɴꜱɪᴅᴇʀ ᴀ ᴍᴏʀᴇ ɢᴇɴᴇʀɪᴄ ꜱᴏʟᴜᴛɪᴏɴ: 通常,创建可以处理任意情况的通用解决方案很有用。如果我这样做,我通常更喜欢可以处理任何格式说明符和任意参数列表的函数。但这对于这个例子来说太过分了。

    #include <stdio.h>
    
    /* 
     * This variant of the function returns pointer 
     * via the function return value.  Caller frees.
     */
    static char *getFilename1(const char *fmt, long n)
    
        size_t size = snprintf(NULL, 0, fmt, n);
        char *buf = malloc(size);
        if (buf != null)
            snprintf(buf, size, fmt, n);
        return buf;
    
    
    /* 
     * This variant of the function returns the pointer 
     * via a function argument.  Caller frees.
     */
    static int getFilename2(char **bufp, const char *fmt, long n)
    
        size_t size = snprintf(NULL, 0, fmt, n);
        if ((*buf = malloc(size)) != null) 
            snprintf(buf, size, fmt, n);
            return 0;
        
        return -1;
    
    
    int
    main(int argc, char **argv) 
    
        char *filename;
        int n = 0;
    
        if (argc > 1) 
            n = atoi(argv[1]);
    
        /* Obtaining buffer pointer as return value */
    
        if ((filename = getFilename1("file#%d", n)) == null) 
             fprintf(stderr, "Out of memory");
             return EXIT_FAILURE;
        
        printf("Filename = %s\n", filename);
        free(filename);
    
        /* Obtaining pointer via function argument */
    
        if ((getFilename2(&filename, "file#%d", n) < 0) 
             fprintf(stderr, "Out of memory");
             return EXIT_FAILURE;
        
        printf("Filename = %s\n", filename);
        free(filename);
    
        return EXIT_SUCCESS;
    
    

【讨论】:

以上是关于Cpp 检查显示缓冲区“tmpf”在其旧内容被使用之前被写入的主要内容,如果未能解决你的问题,请参考以下文章

如何在cpp中解析从套接字捕获的数据包?

CPP流类库与输入输出

对象以编程方式移动后的Fabricjs在其新位置上不可选择

dmesg命令

如何设置 cpp_cp 检查选项?

对象数组打印空白字符串