【问题标题】:c strcat overwrite source string?c strcat 覆盖源字符串?
【发布时间】:2017-01-24 00:07:29
【问题描述】:

我是一名努力学习 C 的 Java 程序员。特别是,我很难理解 strcat()。如果我打电话:

strcat(dst, src);

我知道 strcat() 将修改我的 dst 字符串。但它不应该单独留下 src 字符串吗?考虑下面的代码:

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

void printStuff(char* a, char* b){
        printf("----------------------------------------------\n");
        printf("src: (%d chars)\t\"%s\"\n",strlen(a),a);
        printf("dst: (%d chars)\t\"%s\"\n",strlen(b),b);
        printf("----------------------------------------------\n");
}

int main()
{
        char src[25], dst[25];
        strcpy(src,  "This is source123");
        strcpy(dst,  "This is destination");

        printStuff(src, dst);
        strcat(dst, src);
        printStuff(src, dst);

        return 0;
}

在我的 Linux 机器上产生这个输出,用 GCC 编译:

----------------------------------------------
src: (17 chars) "This is source123"
dst: (19 chars) "This is destination"
----------------------------------------------
----------------------------------------------
src: (4 chars)  "e123"
dst: (36 chars) "This is destinationThis is source123"
----------------------------------------------

我假设完整的“This is source123”字符串仍在内存中,并且 strcat() 已将 char* src 指针前移 13 个字符。但为什么?为什么是 13 个字符?我玩过 dst 字符串的长度,它肯定会在 strcat() 完成后对 src 指针产生影响。但我不明白为什么......

另外...你将如何在 GDB 中调试它?我尝试“step”进入 strcat() 函数,但我猜调试器没有分析该函数; “step”什么也没做。

谢谢! -ROA

PS - 一个简短的说明,我确实阅读了该网站上类似的 strcat() 帖子,但没有看到似乎直接适用于我的问题的帖子。如果我错过了那个帖子,我们深表歉意。

【问题讨论】:

  • "我假设完整的 "This is source123" 字符串仍在内存中" --> 也许。一旦代码在其沙箱之外播放(写入数组边界之外),任何事情都可能发生 - 未定义的行为 (UB)。不要期望“但它不应该单独留下 src 字符串吗?”今天的结果和解释可能有意义,但明天的结果可能会有所不同。
  • 您只为dst 分配了一个char[25]。您希望如何在其中放置 37 个字符(包括尾随的 null)?
  • src + dst 连接的终止符超过了您使用 dst[25] 分配的 24 + 1 个终止符
  • 注意:src 被覆盖的事实是一个巧合,因为它恰好被分配在内存中(在堆栈上)的一个地址,该地址就在分配 dst 之后。因此,持续到 dst 的末尾将覆盖 src。如果切换声明的顺序,结果可能会有所不同:您可能会改写堆栈帧,包括函数的返回地址和保存的寄存器。
  • @M.M 嗯……“它会复制周围的字符。”嗯。我得考虑一下。不确定在这种情况下这会如何影响我的 src 字符串。不过谢谢!

标签: c strcat


【解决方案1】:

您的目的地没有分配足够的内存来保存新的串联字符串。在这种情况下,这意味着 src 可能被 strcat 覆盖,因为它超出了 dst 的范围。

为 dst 分配足够的内存,它应该可以在不覆盖源字符串的情况下工作。 请注意,保存连接字符串的新内存段需要至少是两个字符串的大小(在您的情况下为 36)加上空终止符的空间。

【讨论】:

  • 值得一提的是,这种错误是一个非常常见的安全问题,而strncat 本身就是一个安全问题,因为它的行为并不像人们期望的那样。
  • @Mgetz 我想说 strncat 的行为符合人们的预期。您是否将它与 strncpy 混淆了?
  • @hyde,我想说人们倾向于期望传递给strncat() 的长度参数代表目标缓冲区的总大小,或者可能会小于一个,这本来是明智的设计。相反,它代表了要传输的字符数的上限,这不仅令人惊讶,而且更难使用。
  • @hyde:strncat() 的长度参数是多少?如果您要说“目标缓冲区的长度”,那么这就是为什么strncat() 是一个可怕的接口并且容易引起麻烦的原因。如果您足够了解能够安全地使用strncat(),则可以安全地使用strcpy()memmove()memcpy(),而且效率更高。
  • @hyde:是的,strncat() 的接口很古怪。但是,正如您所说,如果目标字符串为空并且您使用 strncat(was_empty_target_string, source_string, sizeof(was_empty_target_string));,那么 strncat() 会按预期工作。任何其他情况,它不会。
【解决方案2】:

是的,如果您的背景是严格的 Java,我确信与手动内存管理有关的所有事情都会遇到一些困难。

关于任何与 C 字符串相关的内容,将你所知道的关于 Java Strings 的一切都抛在脑后可能会很有用。与 C 字符串最接近的 Java 类似物是 char[]byte[]。然而,即使在那里你也会遇到麻烦,因为 Java 会为你执行边界检查,而 C 不会。事实上,C 允许你做各种你不应该做的事情,同时站在后面静静地喃喃自语,“谁知道如果你这样做会发生什么?”。

特别是,当您调用strcat() 或任何其他写入char 数组的函数时, 负责确保目标数组中有足够的空间来容纳字符.如果没有,则结果行为是未定义的(谁知道 what 会发生什么?)。您只是执行了这种未定义的行为。

一般来说,您需要做以下一件或多件事情:

  • 对可能需要的大小有一个硬性上限,并至少分配那么多空间,或者
  • 知道您有多少空间,并在该空间内工作(例如,截断任何多余的空间),或
  • 跟踪您拥有多少空间以及需要多少空间,并根据需要分配更多空间(确保稍后在您不再需要时释放所有动态分配的空间)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-05
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 2012-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多