32

GCC 8 添加了-Wstringop-truncation警告。来自https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82944

在 GCC 8.0 中通过 r254630 为错误 81117 添加的 -Wstringop-truncation 警告专门用于突出可能意外使用 strncpy 函数,该函数从源字符串中截断终止 NUL 字符。请求中给出的此类滥用的示例如下:

char buf[2];

void test (const char* str)
{
  strncpy (buf, str, strlen (str));
}

我收到与此代码相同的警告。

strncpy(this->name, name, 32);

warning: 'char* strncpy(char*, const char*, size_t)' specified bound 32 equals destination size [-Wstringop-truncation`]

考虑到this->nameischar name[32]nameis a的长度可能大于 32。如果它大于char*32,我想复制name并截断它。应该是 31 而不是 32?我很困惑。NUL 终止不是强制性的。this->namesize_tthis->name

4

8 回答 8

30

此消息试图警告您,您正在做的正是您正在做的事情。很多时候,这不是程序员的本意。如果这是您想要的(意思是,您的代码将正确处理字符数组最终不会包含任何空字符的情况),请关闭警告。

如果您不想或无法在全局范围内关闭它,您可以按照@doron 的指示在本地关闭它:

#include <string.h>
char d[32];
void f(const char *s) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    strncpy(d, s, 32);
#pragma GCC diagnostic pop
}
于 2018-05-06T10:33:21.413 回答
6

这个新的 GCC 警告strncpy()在许多项目中几乎无法使用:代码审查将不接受产生警告的代码。但是如果strncpy()仅与足够短的字符串一起使用,以便它可以写入终止的零字节,那么在开始时将目标缓冲区清零,然后再将其清零strcpy()将实现相同的工作。

实际上,strncpy()是其中一个函数,他们最好不要放入 C 库中。当然,它有合法的用例。但是库设计者也忘记将固定大小的字符串感知对应物strncpy()放入标准中。最重要的此类功能strnlen()strndup()仅在 2008 年被包含在 POSIX.1 中,在strncpy()创建几十年后!并且仍然没有将strncpy()生成的固定长度字符串复制到具有正确 C 语义的预分配缓冲区中的功能,即始终写入 0 终止字节。一种这样的功能可能是:

// Copy string "in" with at most "insz" chars to buffer "out", which
// is "outsz" bytes long. The output is always 0-terminated. Unlike
// strncpy(), strncpy_t() does not zero fill remaining space in the
// output buffer:
char* strncpy_t(char* out, size_t outsz, const char* in, size_t insz){
    assert(outsz > 0);
    while(--outsz > 0 && insz > 0 && *in) { *out++ = *in++; insz--; }
    *out = 0;
    return out;
}

我建议为 使用两个长度输入strncpy_t(),以避免混淆:如果只有一个size参数,则不清楚是输出缓冲区的大小还是输入字符串的最大长度(通常少一个) .

于 2019-10-04T14:02:12.690 回答
4

使用strncpy. 这是一个相当危险的功能。如果源字符串长度(不含空字符)等于目标缓冲区大小,则strncpy不会在目标缓冲区末尾添加空字符。所以目标缓冲区不会被空终止。

我们应该在 Linux 上编写这样的代码:

lenSrc = strnlen(pSrc, destSize)
if (lenSrc < destSize)
    memcpy(pDest, pSrc, lenSrc + 1);
else {
    /* Handle error... */
}

在您的情况下,如果您想在复制时截断源,但仍需要一个空终止的目标缓冲区,那么您可以编写这种代码:

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp);
pDest[sizeCp] = '\0';

编辑:哦...如果这不是强制以 NULL 终止的,strncpy那么使用正确的功能。是的,你需要用 32 而不是 31 来调用它。我认为你需要通过禁用它来忽略这个警告......老实说,我对此没有一个好的答案......

Edit2:为了模仿该strncpy功能,您可以编写以下代码:

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp + 1);
于 2018-05-06T09:54:01.677 回答
2

TL;DR:处理截断情况,警告将消失。


这个警告碰巧对我非常有用,因为它发现了我的代码中的一个问题。考虑这个清单:

#include <string.h>
#include <stdio.h>

int main() {
    const char long_string[] = "It is a very long string";
    char short_string[8];
    
    strncpy(short_string, long_string, sizeof(short_string));

    /* This line is extremely important, it handles string truncation */
    short_string[7] = '\0';

    printf("short_string = \"%s\"\n", short_string);

    return 0;
}

demo

正如评论所说short_string[7] = '\0';,这里是必要的。来自strncpy男人:

警告:如果 src 的前 n 个字节中没有空字节,则放在 dest 中的字符串不会以空值结尾。

如果我们删除这一行,它会调用 UB。例如,对我来说,程序开始打印:

short_string = "这是一个很长的字符串"

基本上,GCC 希望你修复 UB。我在我的代码中添加了这样的处理并且警告消失了。

于 2021-02-10T16:00:05.847 回答
1

其他人的回答让我只写了一个简单的 strncpy 版本。

    #include<string.h>

    char* mystrncpy(char* dest, const char*src, size_t n) {
        memset(dest, 0, n);
        memcpy(dest, src, strnlen(src, n-1));
        return dest;
     }

它避免了警告并保证 dest 为空终止。我正在使用 g++ 编译器并希望避免杂注条目。

于 2021-10-01T16:29:57.627 回答
0

我发现抑制警告的最佳方法是将表达式放在括号中,就像这个 gRPC 补丁

(strncpy(req->initial_request.name, lb_service_name,
         GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH));

诊断抑制解决方案的问题#pragma在于,当编译器无法识别编译指示或特定警告时,#pragma 本身会引发警告;它也太冗长了。

于 2019-05-31T22:32:02.400 回答
0

它说的是我们只能使用 len - 1 个字符,因为最后一个字符应该是 '\0',所以 use 似乎清除了我们只能复制 len - 1 ...

通过例子:

strncpy(this->name, name, 31);

或者

#include <string.h>
char d[32];
void f(const char *s) {
    strncpy(d, s, 31);
}
d[31] = '\0';
于 2020-10-14T18:47:03.293 回答
0

我在寻找这个问题的近乎完美的解决方案时发现了这一点。由于这里的大多数答案都描述了在不抑制警告的情况下如何处理的可能性和方法。接受的答案建议使用以下包装器,这会导致另一组警告并且令人沮丧且不可取。

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    ...
#pragma GCC diagnostic pop

相反,我找到了这个可行的解决方案,不能说是否有任何陷阱,但它可以很好地工作。

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
    strncpy(d, s, 32);
_Pragma("GCC diagnostic pop")

在这里查看全文。

于 2021-03-16T19:33:36.937 回答