te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c - snprintf directive output may be truncated writing up to 20 bytes into a region of size - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - snprintf directive output may be truncated writing up to 20 bytes into a region of size - Stack Overflow

programmeradmin2浏览0评论

Consider the following C code:

#include <inttypes.h> //PRIu64
#include <stdio.h>

#define PATH_MAX 260 
int main()
{
    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char mydir_path[PATH_MAX] = "";
    char other_path[PATH_MAX] = "";

    snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
    printf("%s\n", mydir_path);

    return 0;
}

If I build this with gcc on MINGW64 (gcc.exe (Rev2, Built by MSYS2 project) 14.2.0), Windows 10, I get the following warnings (same warnings can be obtained if you also use onlinegdb to try this example):

$ gcc -g -Wall -std=c99 test.c -o test.exe
test.c: In function 'main':
test.c:11:51: warning: '%s' directive output may be truncated writing up to 20 bytes into a region of size between 0 and 259 [-Wformat-truncation=]
   11 |     snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
      |                                                   ^~               ~~~~~
test.c:11:5: note: 'snprintf' output between 2 and 281 bytes into a destination of size 260
   11 |     snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now, this puzzles me a lot, because I thought that by using snprintf, I'm already hinting to the compiler that I'm aware that a truncation might occur, so I expected it would not notify me about that.

Which is why I am at a loss: what problem is the compiler trying to notify me of here, and how should I anize this code, so it compiles without warning (preferably without switching off, in this case, -Wformat-truncation)?

Consider the following C code:

#include <inttypes.h> //PRIu64
#include <stdio.h>

#define PATH_MAX 260 
int main()
{
    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char mydir_path[PATH_MAX] = "";
    char other_path[PATH_MAX] = "";

    snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
    printf("%s\n", mydir_path);

    return 0;
}

If I build this with gcc on MINGW64 (gcc.exe (Rev2, Built by MSYS2 project) 14.2.0), Windows 10, I get the following warnings (same warnings can be obtained if you also use onlinegdb to try this example):

$ gcc -g -Wall -std=c99 test.c -o test.exe
test.c: In function 'main':
test.c:11:51: warning: '%s' directive output may be truncated writing up to 20 bytes into a region of size between 0 and 259 [-Wformat-truncation=]
   11 |     snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
      |                                                   ^~               ~~~~~
test.c:11:5: note: 'snprintf' output between 2 and 281 bytes into a destination of size 260
   11 |     snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now, this puzzles me a lot, because I thought that by using snprintf, I'm already hinting to the compiler that I'm aware that a truncation might occur, so I expected it would not notify me about that.

Which is why I am at a loss: what problem is the compiler trying to notify me of here, and how should I anize this code, so it compiles without warning (preferably without switching off, in this case, -Wformat-truncation)?

Share Improve this question edited Feb 17 at 17:23 sdbbs asked Feb 17 at 17:16 sdbbssdbbs 5,4467 gold badges57 silver badges118 bronze badges 4
  • OK, seems I found duplicates here stackoverflow/questions/57780368/… and here stackoverflow/questions/71013186/… - though with different possible answers – sdbbs Commented Feb 17 at 17:28
  • You have set the size of the buffer at MAX_PATH, but you are then (possibly) trying to write MAX-PATH plus ` 1 (backslash) + 20 characters to it. The compiler is just warning you that you may end up with a truncated string. The size limit to snprintf only takes effect at run time when the function writes the maximum characters into the buffer. – OldBoy Commented Feb 17 at 17:29
  • Could char mydir_path[PATH_MAX + sizeof mydir + 1] = ""; be used? – xing Commented Feb 17 at 18:37
  • In fact, @xing, even mydir_path[PATH_MAX + sizeof mydir] would be enough in this case, because PATH_MAX and sizeof mydir both account for a terminator, but you need only one. – John Bollinger Commented Feb 17 at 19:13
Add a comment  | 

3 Answers 3

Reset to default 5

Now, this puzzles me a lot, because I thought that by using snprintf, I'm already hinting to the compiler that I'm aware that a truncation might occur, so I expected it would not notify me about that.

Well, yes and no. By using snprintf(), you're most directly ensuring that no more than the specified number of bytes is written. Yes, that conveys a willingness for the output to be truncated, but that does not necessarily mean that you consider truncation acceptable to the point of not being noteworthy.

For instance, it is relatively common practice in some circles to use snprintf() and similar bounded-output functions because they are perceived to be safer than some of their alternatives, such as sprintf(). Such a policy is mostly a hedge against sloppy programming, where the intent is not to accept that truncation is ok, but rather to prefer truncation over a bounds overrun.

Which is why I am at a loss: what problem is the compiler trying to notify me of here, and how should I anize this code, so it compiles without warning (preferably without switching off, in this case, -Wformat-truncation)?

The compiler is warning you about exactly what you think it is warning you about: the string emitted by the snprintf() call may be truncated relative to the one that would be produced into a larger output buffer. Your expectation that the compiler would not warn about this for snprintf() simply is not satisfied. If you don't want to be warned then either ensure that the output buffer is large enough to avoid the possibility of truncation:

    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char other_path[PATH_MAX] = "";
    char mydir_path[sizeof(other_path) + sizeof(mydir)] = "";

    snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir );

... or ensure that the compiler does not have enough information to compute the maximum needed size at compile time (difficult with only one translation unit), or disable the warning.

If you choose to disable the warning, then it may be that you can make that more palatable by doing so only for that particular function call. For instance, GCC has pragmas for locally enabling and disabling warnings. Of course, like most aspects of diagnostic messaging, this is implementation specific.

mydir_path could be defined as a pointer.
Use snprintf to get the size of the output, then allocate memory.

#include <stdio.h>
#include <stdlib.h>

#define PATH_MAX 260
int main()
{
    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char other_path[PATH_MAX] = "";
    char *mydir_path = NULL;

    size_t size = snprintf ( NULL, 0, "%s/%s", other_path, mydir);
    if ( NULL != ( mydir_path = malloc ( size + 1))) { // + 1 for terminating zero
        snprintf ( mydir_path, size, "%s/%s", other_path, mydir);
        printf ( "%s\n", mydir_path);
        free ( mydir_path);
    }
    else {
        fprintf ( stderr, "problem malloc\n");
    }

    return 0;
}

Another option would be to use the precision field to limit the number of characters printed.
The specifier %.*s allows for a variable for the number of characters for the precision.

#include <stdio.h>

#define PATH_MAX 260
int main()
{
    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char other_path[PATH_MAX] = "";
    char mydir_path[PATH_MAX] = "";

    int limit = PATH_MAX - 1 - (int)sizeof mydir; // - 1 for the / in the format string
    snprintf ( mydir_path, sizeof mydir_path, "%.*s/%s", limit, other_path, mydir);
    printf ( "%s\n", mydir_path);

    return 0;
}

Another option to "silence" warning is to handle situation when buffer is to small.

snprintf returns The number of characters that would have been written if n had been sufficiently large, not actually written.

#include <inttypes.h> //PRIu64
#include <stdio.h>

#define PATH_MAX 260 
int main()
{
    char mydir[] = "HEH_SO_TRYINIT_HERE";
    char mydir_path[PATH_MAX] = "";
    char other_path[PATH_MAX] = "";

    if (snprintf( mydir_path, sizeof(mydir_path), "%s/%s", other_path, mydir ) > (int)sizeof(mydir_path)) {
        printf("error\r\n");
    }
    printf("%s\n", mydir_path);

    return 0;
}

In that case compiler knows you have handled both cases - when whole string fits into buffer and when it's truncated.

发布评论

评论列表(0)

  1. 暂无评论