【问题标题】:JOIN function in c language, valgrind problemc语言中的JOIN函数,valgrind问题
【发布时间】:2019-05-23 21:37:50
【问题描述】:

我编写了一个函数,它尝试使用动态内存分配连接两个字符串。我可以理解,如果我使用定义的 char 数组会更容易,但我会学习如何在 C 中管理内存。

问题很简单...用 valgrind 运行程序我发现了不同的错误,如下所示:

  • 大小为 1 的无效写入
  • 大小为 1 的读取无效
  • 地址 .... 为 0 字节 [在|内部] 大小为 4 的块已分配。

我试图找出问题所在,我做到了,但我不明白出了什么问题。

解决这个问题对我来说很重要,所以我可以继续做作业并检查错误。

这是代码

void _join(char *s1, char *s2)
{
    int i = 0;
    int len = strlen(s1);
    while (i < strlen(s2)){
        s1 = realloc(s1, (len + i)*sizeof(char));
        s1[len + i]= s2[i];
        i++;
    }
    // I insert that after, but I don't know if it's necessary
    s1 = realloc(s1, (len + i + 1)*sizeof(char));
    s1[len + i] = '\0';
}

代码从第 48 行到第 58 行。

我是这样从main调用函数

_join(s1, s2);

其中 s1 和 s2 由输入给出。然后我在 s1 和 s2 上调用 free 函数,因为我在那里用 malloc 创建了。

valgrind 输出的 sn-ps

Before: hello
==11005== Invalid write of size 1
==11005==    at 0x109322: _join (changeJOIN.c:53)
==11005==    by 0x1091E9: main (changeJOIN.c:22)
==11005==  Address 0x4a41be4 is 0 bytes after a block of size 4 alloc'd
==11005==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==11005==    by 0x1092FB: _join (changeJOIN.c:52)
==11005==    by 0x1091E9: main (changeJOIN.c:22)
==11005==

...其他错误...

==11005==  
==11005== HEAP SUMMARY:
==11005==     in use at exit: 9 bytes in 1 blocks
==11005==   total heap usage: 17 allocs, 17 frees, 2.109 bytes allocate
==11005==
==11005== 9 bytes in 1 blocks are definitely lost in loss record 1 of 1
==11005==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==11005==    by 0x10935B: _join (changeJOIN.c:56)
==11005==    by 0x1091E9: main (changeJOIN.c:22)

【问题讨论】:

  • 您可以realloc(s1, len + sizeof(s2) +1); 然后循环(或memcpy)将s2 复制到s1 中,而不是多次调用realloc。节省大量时间。
  • 您正在分配 N 字节并将其写入过去。对于这样大小的数组,您可以使用的最大索引是多少?
  • 首先,您的函数不返回char,尽管它的原型。其次,效率非常低。第三,也是最重要的,realloc 使s1 的原始值无效,但更改不会传播给调用者。一个简单的解决方案:将s1 作为char* 返回并在调用者处释放它。
  • 更糟糕的是,它可能会使用在realloc 之后为s1 分配的相同存储空间,让您认为您的程序在它不工作的情况下可以工作。
  • 我忘了strcat。这是将s2 附加到调整大小的s1 的另一个简单解决方案

标签: c valgrind


【解决方案1】:

更简洁的实现:

char *_join(char *s1, char *s2)
{
    size_t len1 = strlen(s1);
    size_t len2 = strlen(s2);
    s1 = realloc(s1, len1 + len2 + 1); // +1 for null terminator.
    memcpy(s1 + len1, s2, len2 + 1);

    return s1;
}

这应该可以解决您的 valgrind 问题,并且更清洁。

确保将其称为 s1 = _join(s1, s2),因为 realloc 将使 s1 无效。

【讨论】:

  • 值得注意的是,全局命名空间中标识符的前下划线在实现之外是禁止的。就虫子而言,它很少咬人,但当它发生时,它会带来非常奇怪的结果,这是一个非常令人讨厌的惊喜。更多关于What are the rules about using an underscore in a C++ identifier?
  • @ABsintioLM 你可以void_join(char *&amp; s1, char *s2)void _join(char ** s1, char *s2) 并通过引用传递s1 的指针以便你可以更新它,但否则_join 中的s1 是一个副本调用者使用的参数。更新副本不会更新参数。听起来很傻,我知道,但是char *s1 传递了引用指向的缓冲区,但指针本身仍然是值传递。
  • @ABsintioLM 在这种情况下你需要返回 s1,因为 realloc 可能决定在其他地方分配更大的内存块,在这种情况下指针不再是 vlaid
  • 如果当前分配的内存块不能增长,realloc 将需要在其他地方分配一个。它确实会复制内容,但地址会改变。
  • @ABsintioLM 假设 s1 有 10 个字符长,存储在地址 1000。现在假设您有另一个字符串 s 存储在地址 1010。现在,假设您尝试重新分配 s1 20 个字符长。它不能使用与之前相同的内存位置,因为那样会与s重叠,所以它需要在其他地方找到内存。
猜你喜欢
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-21
  • 1970-01-01
  • 1970-01-01
  • 2022-06-29
相关资源
最近更新 更多