【问题标题】:malloc() to concatenate 2 strings into third string - crash after compilationmalloc() 将 2 个字符串连接成第三个字符串 - 编译后崩溃
【发布时间】:2014-07-01 18:18:25
【问题描述】:

所以我正在完成“Sams 每天一小时自学 C 编程,第七版”第 10 课练习 7,它要求“编写一个接受两个字符串的函数。使用 malloc() 函数分配足够的内存在连接(链接)后保存两个字符串。返回一个指向这个新字符串的指针。"

我确信有比我在下面尝试的更优雅的方法来解决这个问题。我最感兴趣的是为什么我的解决方案不起作用。我只学了几个月的 C 语言,并没有重要的编程背景。请让我知道为什么这会在编译时崩溃。我在 Win 7 上使用带有 GNU GCC 编译器的代码块,如果这有所作为的话。谢谢你:)

#include <stdio.h>
#include <stdlib.h>
char * concatenated(char array1[], char array2[]);
int ctrtotal;

int main(void)
{
    char *comboString;
    char *array1 = "You\'re the man ";
    char *array2 = "Now Dog!";
    comboString = (char *)malloc(ctrtotal * sizeof(char));

    concatenated(array1, array2);

    if (comboString == NULL)
    {
        puts("Memory error");
        exit(1);
    }
    puts(comboString);
    free(comboString);

    return 0;
}

char * concatenated(char array1[], char array2[])
{
    char *array3;
    int ctr;
    int ctr2;

    for (ctr = 0; array1[ctr] != '\0'; ctr++)
        array3[ctr] = array1[ctr];

    ctr2 = ctr;

    for (ctr = 0; array2[ctr] != '\0'; ctr++)
    {
        array3[ctr2 + ctr] = array2[ctr];
    }

    array3[ctr2 + ctr + 1] = '\0';
    ctrtotal = (ctr2 + ctr + 2);

    return array3;
}

感谢您的帮助。在查看了大家对我的错误的反馈后,我将代码修改为以下内容:

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

char * concatenated(char array1[], char array2[]);

int main(void)
{
    char *array1 = "Testing Testing One Two ";
    char *array2 = "Three.  Finally, not crashing the mem o ry.";
    char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));

    comboString = concatenated(array1, array2);

    if (comboString == NULL)
    {
        puts("Memory error");
        exit(1);
    }

    puts(comboString);
    free(comboString);

    return 0;
}

char * concatenated(char array1[], char array2[])
{
    char *array3;
    array3 = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) );

    strcat(array3, array1);
    strcat(array3, array2);

    return array3;
}

如果有人看到可以/应该删除的任何冗余/不必要的剩余代码,请告诉我。我认识到尽可能简洁的好处。

【问题讨论】:

  • char *array3; 您在使用它时从未为其分配内存。我对你的comboString 的分配也没有信心——你不是忘记为终止的 null 分配一个额外的插槽吗?
  • 你也可以使用ctrtotal 而不给它分配任何东西。
  • 是我还是 ctrtotal 从未正确初始化?你只有int ctrtotal; 所以我很不确定(char *)malloc(ctrtotal * sizeof(char)); 会做什么......
  • @smagnan ctrtotal 在程序加载时被初始化为零,所以他的代码将malloc 零字节。

标签: c string malloc concatenation


【解决方案1】:

你的代码有很多问题:

  • int ctrtotal 永远不会被初始化,所以你是 mallocing 0 个字节
  • concatenated() 正在将字符复制到 未初始化 array3。该指针应指向mallocd 缓冲区。
  • 如果concatenated 正在分配内存,那么main 不需要。相反,它应该使用concatenated 的结果。

我不想给你完整的代码,让你错过这个学习机会。所以concatenated 在伪代码中应该是这样的:

count = length_of(string1) + length_of(string2) + 1
buffer = malloc(count)
copy string1 to buffer
copy string2 to buffer, after string1
set the last byte of buffer to '\0' (NUL)
return buffer

在 C 中,字符串表示为 NUL-terminated array of characters。这就是为什么我们分配一个额外的字节,并以\0 结束它。


附带说明,在处理字符串时,使用指针要容易得多,而不是将它们视为数组并通过索引访问它们。

这里有很多没有任何意义的代码。我建议你先把这个程序写在纸上。然后,在你的脑海中“执行”程序,逐行执行。如果您遇到不理解的内容,那么您需要修复您的理解或错误的代码。不要尝试编写看起来像其他代码的代码。


还有一个名为strcat 的库函数,这将使这项任务变得更加容易。看看你能不能在这里弄清楚如何使用它。

Spoiler -->                                                                                         #include <stdio.h>
                                                                                                    #include <stdlib.h>
                                                                                                    #include <string.h>

                                                                                                    char *concatenate2(const char* s1, const char* s2);

                                                                                                    int main(void)
                                                                                                    {
                                                                                                        char *comboString;
                                                                                                        char *array1 = "You're the man ";
                                                                                                        char *array2 = "Now Dog!";

                                                                                                        comboString = concatenate2(array1, array2);

                                                                                                        if (comboString == NULL)
                                                                                                        {
                                                                                                            puts("Memory error");
                                                                                                            exit(1);
                                                                                                        }
                                                                                                        puts(comboString);
                                                                                                        free(comboString);

                                                                                                        return 0;
                                                                                                    }

                                                                                                    char *concatenate2(const char* s1, const char* s2)
                                                                                                    {
                                                                                                        char *result;

                                                                                                        result = malloc(strlen(s1) + strlen(s2) + 1);

                                                                                                        *result = '\0';

                                                                                                        strcat(result, s1);
                                                                                                        strcat(result, s2);

                                                                                                        return result;
                                                                                                    }

【讨论】:

  • strlen(string1) + strlen(string2) 不会还是一个字符太短吗? strlen doesn't seem to count the trailing null,表示为string1 的内容+string2 的内容分配了足够的空间,但不足以容纳尾随的空值。
  • 调用代码需要捕获来自concatenated()的返回并使用它。
  • 感谢大家的反馈。这些论坛的活跃程度令人惊讶。这真的很有帮助。
  • &lt;pedantic&gt;Stack Overflow is not a forum.&lt;/pedantic&gt;
  • @Derekec 没问题,我只是指出那个链接,以展示 SO 的不同之处(而且更有用!)
【解决方案2】:

您忘记为第三个连接的字符数组分配内存(在函数中) 你应该这样做:

char *array3;
array3 = (char *)malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) ); // +1 for '\0' character.

然后将字符从第一个和第二个数组写入第三个。

【讨论】:

    【解决方案3】:

    也许浏览一下问题代码是最好的。

    #include <stdio.h>
    #include <stdlib.h>
    char * concatenated(char array1[], char array2[]);
    int ctrtotal;
    

    请注意,上面的行将 ctrtotal 声明为整数,但没有指定整数的值。

    int main(void)
    {
       char *comboString;
    
       char *array1 = "You\'re the man ";
       char *array2 = "Now Dog!";
       comboString = (char *)malloc(ctrtotal * sizeof(char));
    

    请注意,上面的行分配了内存并将 'comboString' 设置为指向该内存。但是,分配了多少内存?

    (ctrtotal[???] * sizeof(char)[1])

    (??? * 1) 的值是多少?这是个问题。

       concatenated(array1, array2);
    

    上面这行代码的意图是 array1["You\'re the man "] 和 array2["Now Dog!"] 将连接起来形成一个新的字符串["You\'re the man Now Dog! "],将被放置在分配的内存中并返回给调用者。

    很遗憾,这里没有捕获包含字符串的返回内存。例如,也许上面的行应该是:

       comboString = concatenated(array1, array2);
    

    虽然这是有道理的,但对于这行,它引出了一个问题:行的目的:

       comboString = (char *)malloc(ctrtotal * sizeof(char));
    

    以及全局变量:

       int ctrtotal;
    

    以及后面的参考:

       ctrtotal = (ctr2 + ctr + 2);
    

    也许这 3 行都应该删除?

       if (comboString == NULL)
       {
          puts("Memory error");
          exit(1);
       }
    
       puts(comboString);
       free(comboString);
    
       return 0;
    }
    
     char * concatenated(char array1[], char array2[])
       {
       char *array3;
    

    请注意,'*array3' 现在是一个已定义的指针,但它没有指向任何特定的地方。

       int ctr;
       int ctr2;
    

    'concatenated()' 的目的是将array1 和array1 连接到分配的array3 中。不幸的是,没有为array3 分配内存。

    下面,array3指向的内存将被修改。由于 array3 没有指向任何特定的地方,所以这是不安全的。

    在修改数组 3 指向的内存之前,将数组 3 指向可以安全修改字节的内存是很重要的。我建议在此处插入以下代码:

       array3 = malloc(strlen(array1) + strlen(array2) + 1);
    

    现在,array3 指向分配的内存,大到足以容纳两个字符串和字符串终止字符 '\0'。

       for (ctr = 0; array1[ctr] != '\0'; ctr++)
          array3[ctr] = array1[ctr];
    
       ctr2 = ctr;
    
       for (ctr = 0; array2[ctr] != '\0'; ctr++)
       {
          array3[ctr2 + ctr] = array2[ctr];
       }
    
       array3[ctr2 + ctr + 1] = '\0';
       ctrtotal = (ctr2 + ctr + 2);
    
       return array3;
       }
    

    【讨论】:

    • "(One may assume (incorrectly) that the value of ctrtotal is zero. The actual value of ctrtotal is unknown)." 这是不正确的。见this question。全局变量初始化为零。然而,本地(又名堆栈)变量不是。
    • @JonathonReinhart,你是绝对正确的。出于某种原因,我的大脑将变量放在了 main() 中。毕竟,它是全球性的对我来说毫无意义。我已经确定了我的答案。谢谢。
    【解决方案4】:

    我正在回复您修改后的代码。它有一些错误。

    ...
    char *array2 = "Three.  Finally, not crashing the mem o ry.";
    char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));
    
    comboString = concatenated(array1, array2);
    ...
    

    malloc 在这里是不必要的,实际上是代码中的一个错误。您正在分配一块内存,但随后将指针 comboString 的值替换为来自对 concatenated. 的调用的指针您丢失了指向在 main 中分配的内存块的指针,因此永远无法释放它。虽然这在您现在拥有的代码中不会成为问题,因为 main 很快就会返回,但它可能会导致运行时间较长的应用程序出现内存泄漏。

    strcat(array3, array1);
    

    这也是一个错误。 strcat 将遍历array3 以查找'\0',然后一旦找到从该索引开始复制到array1 中,替换'\0'。这在这里工作正常,因为分配给 array3 的内存块将被清零**,因为您的程序尚未释放任何块。但是,在运行时间较长的程序中,您可能会得到一个不以“\0”开头的块。您最终可能会损坏堆、出现段错误等。

    要解决此问题,您应该改用 strcpy,array3[0] = '\0',*array3 = '\0'

    ** 当操作系统启动您的程序时,它将用零初始化它为它保留的内存段(这实际上不是必需的,但几乎在任何操作系统上都是如此)。当您的程序分配和释放内存时,您最终会得到不为零的值。请注意,未初始化的局部变量可能会出现相同的错误,例如:

    int i;
    for (; i < 10; i++);
    

    只要存储i的运行时堆栈上的空间已经为0,此循环就会运行10次。

    总体而言,在 C 中对数组和动态内存分配要非常小心。C 没有为您提供现代语言所提供的任何保护。你有责任确保你保持在数组的范围内,初始化你的变量,并正确分配和释放你的内存。忽略这些事情会导致难以发现的错误,您需要花费数小时才能找到,而且大多数情况下这些错误不会立即出现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-17
      • 2013-05-15
      • 2014-04-19
      相关资源
      最近更新 更多