gcc-8 -Wstringop-truncation有什么好的做法?

时间:2018-05-06 09:44:49

标签: c++ gcc strncpy gcc8

海湾合作委员会8增加了-Wstringop-truncation警告。来自https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82944

  

在GCC 8.0中通过r254630为bug 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->namechar name[32]namechar*,其长度可能大于32.我想将name复制到{{}如果它大于32,则截断它。this->name应该是31而不是32?我糊涂了。 size_t并非强制要求NUL终止。

7 个答案:

答案 0 :(得分:9)

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

如果您不想或不能全局关闭它,可以按照@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
}

答案 1 :(得分:2)

使用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);

答案 2 :(得分:2)

此新的GCC警告使strncpy()在许多项目中几乎不可用:代码审查将不接受代码,并产生警告。但是,如果strncpy()仅与足够短的字符串一起使用,以便它可以写入终止的零字节,然后在开始处将目标缓冲区清零,然后使用普通strcpy()即可完成相同的工作。

实际上,strncpy()是其中之一,最好不要将其放入C库中。当然,有合理的用例。但是库设计人员也忘记了将固定大小的字符串感知副本与strncpy()放入标准中。最重要的此类功能strnlen()strndup()仅在创建strncpy()几十年后才包含到POSIX.1中。而且仍然没有功能,可以将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参数,则不清楚是输出缓冲区的大小还是输入字符串的最大长度(通常小于一)。

答案 3 :(得分: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。我在我的代码中添加了这样的处理,警告消失了。

答案 4 :(得分:0)

我发现抑制警告的最佳方法是将表达式放在括号like this gRPC patch中:

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

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

答案 5 :(得分:0)

它说的是我们只能使用len-1个字符,因为最后一个应该是'\ 0',所以使用似乎可以清除警告,我们只能复制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';

答案 6 :(得分:0)

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

def triplet_loss(y_true, y_pred):
anchor, positive, negative = y_pred[:,:emb_size], y_pred[:,emb_size:2*emb_size], y_pred[:,2*emb_size:]
positive_dist = tf.reduce_mean(tf.square(anchor - positive), axis=1)
negative_dist = tf.reduce_mean(tf.square(anchor - negative), axis=1)
return tf.maximum(positive_dist - negative_dist + alpha, 0.)

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

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

查看全文here