关于 strncpy 使用的 GCC 误报警告?

GCC false positive warning on strncpy usage?

关于 strncpy 警告的另一个问题。

代码如下:

#include <cstring>

extern char g_dest[16];
extern char g_src[16];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    return x;
}

使用 g++ 8.3 版编译它:

$ g++ --version
g++ (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
strncpy-warning2.cxx: In function ‘char* mycopy()’:
strncpy-warning2.cxx:8:24: error: ‘char* strncpy(char*, const char*, size_t)’ output may be truncated copying 15 bytes from a string of length 15 [-Werror=stringop-truncation]
     char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
                ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

警告可以通过以下任一方法解决:

  1. 使 g_src 比 g_dest 短:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char g_src[15];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$
  1. 或者在g_dest末尾明确添加一个NUL终止符:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char g_src[16];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    g_dest[ sizeof( g_dest ) - 1 ] = '[=13=]';
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$
  1. 或者如果我将 g_src 设为指针而不是数组:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char* g_src2;

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src2, sizeof ( g_dest ) - 1 );
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$

我知道 GCC 试图警告 strncpy 使用中的潜在错误,但是,我不明白为什么我们不能对 2 个相同大小的数组执行 strncpy?为什么警告消息说 the output maybe truncated ? (即我找不到 output 可能是 truncated 的例子,除非 GCC 假设 g_src 不一定是有效的 NUL 终止的 c 字符串。)

通过谷歌搜索,我在 GCC bugzilla 中看到了一个类似的案例:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87028,但是,我不确定这是否是同一案例,因为它最终被标记为已修复,但显然是我的问题在 GCC 10.2 中仍然存在。

谢谢!

最好的(最有效、最简单、最容易理解成功和错误情况)是在源和目标大小相同且相当小的情况下使用 memcpy。 GCC 不会对此发出警告。

如果您只是想让 GCC 不抱怨您的 strncpy() 调用,即使它是不安全的,您可以像这样将它括在括号中:

char * x = (strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 ));

GCC 理解这意味着“我知道我在做什么,不要试图拯救我自己。”

传递给strncpy的长度是要复制的最大字节数,包括可能的空终止符,而不是排除空终止符的“字符串长度”。使用 sizeof g_dest,而不是 sizeof ( g_dest ) - 1

如果g_src包含15个非空字符和一个空终止符的字符串,则strncpy(g_dest, g_src, sizeof g_dest - 1);不会复制它;它只会复制 15 个字符而不复制空终止符。它也不会附加一个不存在且在长度内复制的空终止符(尽管它会将空值附加到给定长度)。

这就是编译器警告您的内容。 strncpy 应该传递缓冲区的全长,而不是“字符串长度”。

在这种情况下,我宁愿使用strcpy()。我在其他项目上多次尝试 运行 strncpy() 并且我认为它受到编译器的保护,因为在没有 '[=12=]' 运算符的情况下通过 for-loop 创建字符串的情况。也许我错了,但这个理由对当时的我来说已经足够了。