【问题标题】:using realloc to write on a new string?使用 realloc 在新字符串上写入?
【发布时间】:2022-01-22 23:31:51
【问题描述】:

remove_multiple 采用一个参数(一个 C 字符串),并删除重复项。它必须返回堆中动态分配的字符串。

我尝试过:

  • 创建一个新的动态分配的指针;
  • 如果(str)的当前字符不等于下一个字符,只需在重新分配 (s) 后将其复制到 s 中。

问题是我在 realloc 上仍然有这个警告:“C6308 'realloc' 可能返回空指针:将空指针分配给 's',它作为参数传递给 'realloc',将导致原始内存块被泄露”,当我尝试调试程序时收到一条消息,它说我试图在分配的内存之外写入。

这是我的代码:

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

char* remove_duplicates(const char* str) {
    char* s = malloc(sizeof(char)); 
    if (!s) {
        return NULL; 
    }
    for (size_t i = 0; str[i] != 0; i++) {
        
        
        if (str[i] != str[i + 1]) {
            s = realloc(s, 2 * sizeof(char));
            if (!s) {
                return NULL;
            }
            s[i] = str[i];
        }
    }

    return s; 
}



int main(void) {
    char str[] = "heyyyy"; 
    char* s = remove_duplicates(str); 
    printf("%s", s); 

    free(s); 
    return 0; 
}

错误列表:

  • 警告 C6308 'realloc' 可能返回空指针:将空指针分配给作为参数传递给 'realloc' 的 's' 将导致原始内存块泄漏。

【问题讨论】:

  • 警告非常具体。如果您获得返回值NULL,您将无法再访问旧地址并且无法释放它,因为您已经用NULL 值覆盖了变量。您可以改用temp 变量,如果不是NULL,则仅分配给s
  • realloc(s, 2 * sizeof(char)); 您将永远无法存储超过 2 个字符。这是故意的吗?我没有看到您将索引 i 限制为最大 1。
  • 不,你为什么要分配2?你写信给索引i。这意味着您必须至少分配 i+1 字节。
  • 如果要删除重复字符,还必须使用 2 个索引变量。一个用于源数组,一个用于目标数组。
  • 你需要s = tmp,而不是*s = *tmp。对于少量分配,s = tmp 通常是无操作的,因为它们已经相同,所以这个错误只会偶尔地咬你(在内存压力下)。但是您的代码中还有很多其他问题。

标签: c duplicates c-strings realloc function-definition


【解决方案1】:

对于初学者来说,你的方法并不好。

该函数效率低下,因为其中有太多的内存重新分配。

至于消息然后这个

s = realloc(s, 2 * sizeof(char));

语句不安全,因为函数realloc 可以返回一个空指针。在这种情况下,先前分配的内存地址将丢失,从而导致内存泄漏。

您需要使用一个中间指针,该指针将被分配realloc 返回的值。

在此声明中

s = realloc(s, 2 * sizeof(char));

总是分配 2 个字节的内存。

在左侧表达式中也使用了无效索引

s[i] = str[i];
^^^^

对于指针s,你需要支持它的一个索引而不是i

您首先需要确定有多少不重复的相邻字符,然后只分配一次所需大小的数组。

这是一个演示程序。

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

char * remove_duplicates( const char *s ) 
{
    size_t n = 0;
    
    for ( const char *p = s; *p; )
    {
        ++n;
        while ( *++p && p[0] == p[-1] );
    }
    
    char *result = malloc( n + 1 );
    
    if ( result != NULL )
    {
        char *p = result;
        do
        {
            *p = *s;
            while ( *s++ && s[0] == s[-1] );            
        } while ( *p++ );
    }
    
    return result;
}

int main( void )
{
    const char *s = "1223334444";
    
    printf( "\"%s\"\n", s );
    
    char *p = remove_duplicates( s );
    
    if ( p )    printf( "\"%s\"\n", p );

    free( p );
}

程序输出是

"1223334444"
"1234"

【讨论】:

    【解决方案2】:

    您通常不需要为每个字符重新分配。所以懒惰的方法是先分配一个与原始数组大小相同的数组,然后有选择地尝试在最后缩小它:

    char* remove_duplicates(const char* str) {
        char* s = malloc(1 + strlen(str)); // only allocate once with original size
        if (!s) {
            return NULL;
        }
        // keep track or current position, size and previous character
        char c, old = 0, *curr = s;
        size_t n = 0;
        while ((c = *str++) != '\0') {
            // ignore duplicates
            if (c != old) {
                *curr++ = c;
                old = c;
                ++n;
            }
        }
        // add the terminating null
        *curr = '\0';
        // optional shrink attempt
        s = realloc(s, n +  1);
        return s;
    }
    

    【讨论】:

    • s = realloc(s, n + 1); 不存在内存泄漏的可能性吗?
    • @ryyker:是的,但它是一种惯用的方式。 realloc 允许不做任何更改并返回原始指针(那么没问题......)。如果它成功分配了一个新数组,那么它将释放原始数组。只有在分配新数组失败时才会发生内存泄漏,但由于内存分配错误通常是致命的,所以这并不重要,因为程序已接近尾声。
    • @chux-ReinstateMonica:哎呀...帖子已编辑。感谢您的评论!
    • @chux 我不应该在这里使用大写字母。固定
    猜你喜欢
    • 2015-04-23
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多