【问题标题】:GCC: ‘strncat’ output may be truncated [-Werror=stringop-truncation]. What does it mean and how to fix it?GCC:“strncat”输出可能被截断 [-Werror=stringop-truncation]。这是什么意思以及如何解决它?
【发布时间】:2020-12-03 13:48:05
【问题描述】:

我收到gcc -std=gnu17 -Wall -Werror -Wshadow -O3 test.c 的警告:

In function ‘insertString’,
    inlined from ‘replaceString’ at test.c:94:5,
    inlined from ‘main’ at test.c:110:22:
test.c:69:5: error: ‘strncat’ output may be truncated copying between 0 and 77 bytes from a string of length 80 [-Werror=stringop-truncation]
     strncat(source, buffer, STRING_SIZE - 1 - position - strlen(stringToInsert));
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

main() 中删除do while 循环(不对相关的strncat() 语句进行任何更改)会使警告消失。

  1. 警告是什么意思,为什么会消失?
  2. 我应该在代码中加入哪些更改以使上述 gcc 命令不会触发警告? 解决方案不能简单地禁用警告(使用 fe.#pragma 语句)。 解决方案必须使用 strncat() 函数。

不重要:这是出于学习目的,请进行描述。该程序解决了 Stephen G. Kochan 的“Programming in C (4th Edition)”一书第 9 章的练习 9。

代码:

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

#define STRING_SIZE 81

int findString(const char strToSearch[], const char strSought[])
{
    int strToSearchLength = strlen(strToSearch);
    int strSoughtLength = strlen(strSought);

    for (int i = 0; i <= strToSearchLength - 1; ++i)
    {
        if (strToSearch[i] == strSought[0])
        {
            int j = 0;
            while (strToSearch[i+j] == strSought[j])
            {
                if (strSought[j+1] == '\0')
                {
                    return i;
                }
                ++j;
            }
        }
        else if (i > strToSearchLength - strSoughtLength - 1)
        {
            return -1;
        }
    }

    return -1;
}

bool removeString(char source[], const int start, const int nCharsToRemove)
{
    int i, sourceLength = strlen(source);

    if (start + nCharsToRemove > sourceLength || start < 0 || nCharsToRemove < 0)
    {
        printf("Error in function removeString(): invalid parameters.\n");
        return false;
    }
    else
    {
        for (i = start; i < sourceLength; ++i)
        {
            source[i] = source[i + nCharsToRemove];
        }
        source[i] = '\0';
        return true;
    }
}

void insertString(char source[], const char stringToInsert[], const int position)
{
    char buffer[STRING_SIZE];

    int i = 0;
    while (source[position + i] != '\0' && position + i < STRING_SIZE - 1)
    {
        buffer[i] = source[position + i];
        ++i;
    }
    buffer[i] = '\0';

    source[position] = '\0';
    strncat(source, stringToInsert, STRING_SIZE - 1 - position);
// THE STATEMENT MENTIONED IN THE WARNING:
    strncat(source, buffer, STRING_SIZE - 1 - position - strlen(stringToInsert));
}

/* A function to replace the first occurence of the string s1
 * inside the source string, if it exists, with the string s2
 */
bool replaceString(char source[], const char s1[], const char s2[])
{
    int findString(const char strToSearch[], const char strSought[]);
    bool removeString(char source[], const int start, const int nCharsToRemove);
    void insertString(char source[], const char stringToInsert[], const int position);
    int s1_position;
    bool success;

    // locate s1 inside source
    s1_position = findString(source, s1);
    if (s1_position == -1)
        return false;

    // remove s1 from source
    success = removeString(source, s1_position, strlen(s1));
    if (! success)
        return false;

    // insert s2 into source at the proper location
    insertString(source, s2, s1_position);

    return true;
}

int main(void)
{
    char text[STRING_SIZE] = "1 is first*";

// uncommenting the following comment and discarding what follows it makes the warning go away
/*
    replaceString(text, "is", "one");
    printf("%s\n", text);
*/
    bool stillFound;
    do
        stillFound = replaceString(text, "is", "one");
    while (stillFound);
    printf("%s\n", text);

    return 0;
}

【问题讨论】:

  • 如果知道缓冲区太小在编译时然后使用更大的缓冲区。
  • The solution has to use strncat() function. ?为什么? strlcat 呢?你抄了书里面的东西吗? The code: 这是一个例子,但肯定不是minimalminimal reproducible example 可能会更短,不是吗?。
  • 这能回答你的问题吗? stackoverflow.com/questions/50198319/…
  • @KamilCuk 您发布的链接没有解释错误的含义,触发它的原因,为什么删除do while 循环使其消失以及如何解决问题(如果有的话)。
  • @KamilCuk 使用另一个函数可以绕过这个问题。警告是有原因的,我认为真正了解正在发生的事情是件好事。这是我的代码。我可以看到如何通过用常量替换一些函数来缩短它,但我不知道如何使问题更容易看到,如果您不同意,您有什么建议吗?

标签: c gcc compiler-warnings truncation


【解决方案1】:

警告是什么意思

buffer 可能会指向一个包含 80 个字符的字符串,但长度 STRING_SIZE - 1 - position - strlen(stringToInsert) 将低于 80。创建警告是为了检测 不是全部的情况 em> 源缓冲区将被复制到目标(即在strcat(destination, source) 调用中)。它可能可能发生。在这种情况下,目标缓冲区也不会以零结尾。

为什么会消失?

不同的代码使编译器做出不同的决定。在这种情况下,编译器不会内联调用,这很可能会影响编译器完成的一些静态分析。将staticattribute((always_inline)) 添加到函数会恢复警告。很难准确地回答“为什么”——答案要么太宽泛,要么太详细。我相信检查 gcc 源或 RTL 输出以了解更多信息。

我应该在代码中加入哪些更改以使上述 gcc 命令不会触发警告?

不要使用strncat。使用memcpy。我想到了一些事情:

char *dest = &source[position]; // where we copy to
size_t destfree = STRING_SIZE - 1 - position; // how much space we have there
// helper macro
#define MIN(a, b)  ((a)<(b)?(a):(b))

size_t to_copy = MIN(strlen(stringToInsert), destfree);
memcpy(dest, stringToInsert, to_copy);
dest += to_copy;
destfree -= to_copy;

to_copy = MIN(strlen(buffer), destfree);
memcpy(dest, buffer, to_copy);
dest += to_copy;
destfree -= to_copy;

dest[0] = '\0';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 2021-06-25
    • 2013-08-04
    • 2021-06-29
    • 1970-01-01
    • 2020-04-22
    • 2010-10-08
    相关资源
    最近更新 更多