Search code examples
cprintfgcc-warningformat-truncation

-Wformat-truncation - can I avoid it rather than disable the warning?


There's this code I'm compling, which has the line:

snprintf(target, 11, "%02ld-%02ld-19%02ld", day, month, year);

... which is executed after it has been verified that all 3 values are valid; and specifically, that year is between 0 and 99.

However, recent versions of GCC, when run with -Wextra, complain:

warning: ‘%02ld’ directive output may be truncated writing between 2 and 20 bytes
into a region of size 3 [-Wformat-truncation=]

I would rather not entirely disable this warning; nor even disable it locally. Instead, I am wondering if I could somehow "convince" GCC of the value range for the three arguments, preventing the warning.

Yes, this is rather ugly code that reinvents the wheel, and locale-specific routines for date formatting should be used, no need to lecture me; not my code.


Solution

  • if I could somehow "convince" GCC of the value range for the three arguments, preventing the warning.

    Let the compiler know that all values used are of 1-2 digits with reduced range type, &, %, / or etc.

    In this case, recommend unsigned math and %.
    (Note: recall some_int%100 results are in the range -99 to 99, up to 3 characters, hence the reason for unsigned math.)

    char target[11];
    //                               dd   -dd   -19dd   \0   0-99         0-99        0-99   
    snprintf(target, sizeof target, "%02lu-%02lu-19%02lu", day%100lu, month%100lu, year%100lu);
    

    A smart enough compiler will see % and analyse accordingly.


    Since month and day are in the range 1-12 and 1-31, could use month & 15, day & 31, but that micro optimization lacks clarity.


    If day%100lu brings up an undesirable warning about mixed sign-ness, then

    snprintf(target, sizeof target, "%02u-%02u-19%02u",
        (unsigned)day%100u, (unsigned)month%100u, (unsigned)year%100u);
    

    or simply use a wider target ;-)

    Perhaps the below if a generous buffer is not prohibitive.

    #define CHAR_PER_LONG_N (CHAR_BIT*sizeof(long)/3+3)
    #define DATE_FMT %02ld-%02ld-19%02ld"
    #define BUF_N (sizeof DATE_FMT + 3*CHAR_PER_LONG_N)
    char target[BUF_N];
    
    snprintf(target, sizeof target, DATE_FMT, day, month, year);