【问题标题】:C segfault calling strcat in recursive functionC segfault在递归函数中调用strcat
【发布时间】:2014-04-28 02:36:38
【问题描述】:

我在尝试使用 c 中的 strcat 连接到字符串时遇到了段错误。 gdb中的错误是:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1 (LWP 1)]
0xff1692b4 in strcat () from /lib/libc.so.1

相关代码如下。我在调用函数中malloc-ing传入的char * returnedString,传入returnString的初始值就是“”。

char * printPostOrder(struct block * node, char * returnedString)
 {
 char * temp;
   temp = (char *)malloc(50 * sizeof(char));
  temp[0]='\0';
if(temp){
if (node != NULL)
{
    returnedString = printPostOrder(node -> left,returnedString);
    returnedString = printPostOrder(node -> right,returnedString);

    if (node -> left == NULL || node -> right == NULL)
    {
        if (node -> flag == 1)
        {
            sprintf(temp,"(%dA)\0",node -> size);
            strcat(returnedString,temp);
            free(temp);
        }
        else
        {
            sprintf(temp,"(%dF)\0",node -> size);
            //printf("%c",temp);
            strcat(returnedString,temp);

        }

    }
free(temp);
}
}

return returnedString;

}

任何帮助将不胜感激!

【问题讨论】:

  • 您的基本情况在哪里?您可能内存不足,因为它从不停止创建新节点。你的堆栈太大了。
  • 我不是在制作节点,而是在遍历现有节点,打印出大小。我认为只有在节点不为 NULL 时才会再次调用该函数?
  • 您不需要执行returnedString = strcat(returnedString, ...),因为无论如何strcat 只会返回retrurnedString。所以你实际上是在做returnedString = returnedString;。然而,这只是一个旁注。这不是您的段错误的原因。
  • 当你完成它时,你并没有释放temp。除此之外,是否有足够的节点使returnedString 可能太短?如果新字符串不适合,strcat() 将不会分配更多空间...
  • 现在你释放了两次 temp,但你仍然超出了顶层缓冲区。此外,在节点为 NULL 的情况下,您不会释放 temp。

标签: c recursion segmentation-fault strcat


【解决方案1】:

首先,这两行

char * temp;
temp = (char *)malloc(50 * sizeof(char));

应该换成这一行

char temp[50];

否则,每次调用该函数时都会泄漏 50 字节的内存。

其次,在顶层,returnedString 应该声明为

static char returnedString[1000000];

因为您几乎肯定会超出您 strcat 进入的缓冲区。

作为解释,strcat 的文档说:“字符串 s1 必须有足够的空间来保存结果。”注意s1strcat 的第一个参数,即输出缓冲区。 strcat 不会为您管理内存,也不会使输出缓冲区变大。如果输出缓冲区不够大,它就会崩溃。因此,您需要确保从较大的输出缓冲区开始。你可以malloc那个缓冲区,或者像我上面展示的那样静态声明它,但是你需要确保它足够大来保存最终结果。

【讨论】:

    【解决方案2】:

    假设您为“返回的字符串”分配了足够的内存,我建议这样:

        char * printPostOrder(struct block * node, char * returnedString)
        {
                if (node != NULL)
                {
                        returnedString = printPostOrder(node -> left,returnedString);
                        returnedString = printPostOrder(node -> right,returnedString);
    
                        if (node -> left == NULL || node -> right == NULL)
                        {
                                if (node -> flag == 1)
                                {
                                        returnedString += sprintf(returnedString, "(%dA)",node -> size);
                                }
                                else
                                {
                                        returnedString += sprintf(returnedString, "(%dF)",node -> size);
                                }
                        }
                }
                return returnedString;
        }
    

    snprintf 返回写入的字符数,因此您可以始终保持此指针指向字符串的末尾。每次你在那里写,你有效地追加到原始字符串的末尾。 不过,“假设您为返回的字符串分配了足够的内存”很重要。

    关于 strcat(str0, str1) 的说明:它不知道 str0 的结束位置,因此每次调用它时,它都会遍历 str0 中的字符以查找结束。仅仅更新指向字符串当前结尾的指针对我来说似乎比一直重新计算长度要好得多。

    【讨论】:

    • 如果调用函数对返回值做任何事情,你真的不想做returnedString += sprintf(...)。再说一次,它真的没有必要这样做,因为它可以检查它传入的值。除此之外,我同意它比strcat更好。
    • 好吧,无论如何返回函数保证永远不会改变的东西是没有意义的,使用函数的人会期望:1)要么不返回我给它的char* 2)它返回一些东西,可能是因为它可能改变了谁会依赖一个不改变指针的函数?你可以在没有函数的情况下做nop
    • 我同意一开始就返回字符串是没有意义的。我只是指出,如果调用函数类似于printf("%s\n", printPostOrder(node, str));,那么它将跳过字符串的前几个字节。编辑:返回字符串不是必要的,但许多函数为了方便而这样做,因此函数调用可以作为参数嵌入到另一个函数中,如printf("%s\n", strcat(...));。这就是 strcat 返回与您传入的字符串相同的确切原因。
    • 感谢@BuellaGábor。我已经尝试过了,我得到了一个不同的段错误:[Switching to Thread 1 (LWP 1)] 0xff19a298 in _ndoprnt () from /lib/libc.so.1 (gdb) backtrace #0 0xff19a298 in _ndoprnt () from /lib/libc.so.1 #1 0xff19de58 in sprintf() from /lib/libc.so.1
    • 您的回溯提到了 sprintf,这可能意味着 sprintf 尝试写入内存中的错误位置。我们这里的大多数人都怀疑您最初为 *returnedString 分配的内存太小。可能这应该是动态分配的。另外,如果有调试器,请使用调试器,并逐步观察。
    【解决方案3】:

    一方面,您不会在任何地方致电free(temp)

    最好的情况是这只是吃掉一堆内存,否则不会引起任何问题。

    最坏的情况是(取决于您正在遍历的树的大小以及您有多少可用内存)您实际上内存不足。由于您没有检查malloc() 是否返回NULL,因此您将传递一个NULL 指针到sprintf(),这很可能是您的段错误的原因。

    【讨论】:

    • 我现在正在免费打电话并检查 malloc 是否成功。仍然得到这个问题。我已经更新了上面的代码
    • 在这种情况下,我倾向于认为 Dmitri 的观察可能是正确的。也许您没有为returnedString 分配足够的内存。
    • 顺便说一句,您需要将free(temp) 放在 之后为if (node != NULL) 条件的右括号。换句话说,做if (temp) { if (node != NULL) { ... } free(temp); },而不是if (temp) { if (node != NULL) { ... free(temp); } }
    • 这是我对返回字符串的分配。自从第一次调用它以来,肯定有足够多的内存我只遍历 3 个节点 char * returnedString;返回字符串 = (char *)malloc(10000 * sizeof(char));
    • 您是否使用过调试器来识别它发生段错误的确切行?
    猜你喜欢
    • 2016-11-14
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 2019-03-26
    相关资源
    最近更新 更多