Cpp检查显示缓冲区'tmpf'是在使用旧内容之前编写的

时间:2015-10-01 07:12:51

标签: c cppcheck

此功能在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;
}

1 个答案:

答案 0 :(得分:3)

一些反馈:

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

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

  3. ᴀᴠᴏɪᴅᴛᴇʀᴍɪɴᴀᴛɪɴɢᴩʀᴏɢʀᴀᴍɪɴꜰᴜɴᴄᴛɪᴏɴꜰᴜɴᴄᴛɪᴏɴ:从函数内部退出程序(特别是一个简单的函数)通常是一个坏主意。如果错误是不可恢复的并且退出对于应用程序的性质是可接受的(对于像烤面包机这样的嵌入式系统),最好对错误处理进行战略性处理,将可能致命的错误传播到main(),并根据需要从那里传出。烤箱,或控制塔雷达,它不是)。客户讨厌意外退出的应用程序,它可能会产生严重的后果。不要让任意函数中出现小的不可预见的错误。

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

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

    #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;
    }