Por que estou recebendo um erro livre duplo com realloc ()?

votos
11

Eu tentei escrever um texto de substituição da função em C, que trabalha em um char *, que foi alocado usando malloc(). É um pouco diferente, já que ele irá localizar e substituir cordas, em vez de caracteres na seqüência de partida.

É trivial que fazer se a busca e substituir cordas são o mesmo comprimento (ou o texto de substituição é menor do que a string de pesquisa), desde que eu tenho espaço suficiente alocado. Se eu tentar usar realloc(), eu recebo um erro que me diz que eu estou fazendo um double livre - o que eu não vejo como eu sou, já que eu só estou usando realloc().

Talvez um pouco de código vai ajudar:

void strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while (find = strstr(find, search)) {

        if (delta > 0) {
            realloc(input, strlen(input) + delta);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input));
        memmove(find, replace, replaceLen);
    }
}

O programa trabalha, até que eu tente realloc()em uma instância onde a cadeia substituída será mais longo do que a seqüência inicial. (Ainda tipo de funciona, ele simplesmente cospe erros, bem como o resultado).

Se ajudar, o código de chamada é semelhante a:

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

void strrep(char *input, char *search, char *replace);

int main(void) {
    char *input = malloc(81);

    while ((fgets(input, 81, stdin)) != NULL) {
        strrep(input, Noel, Christmas);
    }
}
Publicado 04/08/2008 em 12:06
fonte usuário
Em outras línguas...                            


8 respostas

votos
12

Primeiro, desculpe, eu estou atrasado para a festa. Esta é a minha primeira resposta stackoverflow. :)

Como já foi salientado, quando realloc () é chamado, você pode potencialmente alterar o ponteiro para a memória que está sendo realocado. Quando isso acontece, o argumento "string" se torna inválida. Mesmo se você transferir isso, a mudança fica fora do escopo uma vez que a função termina.

Para responder a OP, realloc () retorna um ponteiro para a memória recém-realocado. O valor de retorno deve ser armazenado em algum lugar. Geralmente, você faria isso:

data *foo = malloc(SIZE * sizeof(data));
data *bar = realloc(foo, NEWSIZE * sizeof(data));

/* Test bar for safety before blowing away foo */
if (bar != NULL)
{
   foo = bar;
   bar = NULL;
}
else
{
   fprintf(stderr, "Crap. Memory error.\n");
   free(foo);
   exit(-1);
}

Como aponta TyBoer, vocês não pode alterar o valor do ponteiro sendo passado como entrada para esta função. Você pode atribuir o que quiser, mas a mudança vai sair do âmbito no final da função. No bloco seguinte, "input" pode ou não ser um ponteiro inválido uma vez que a função completa:

void foobar(char *input, int newlength)
{
   /* Here, I ignore my own advice to save space. Check your return values! */
   input = realloc(input, newlength * sizeof(char));
}

Mark tenta contornar este retornando o novo ponteiro, como a saída da função. Se você fizer isso, a responsabilidade recai sobre o chamador para usar nunca mais o ponteiro ele usou para entrada. Se ele corresponder o valor de retorno, então você tem dois ponteiros para o mesmo local e só precisa chamar free () em um deles. Se eles não corresponderem, o ponteiro de entrada agora aponta para memória que pode ou não ser de propriedade do processo. Dereferencing que poderia causar uma falha de segmentação.

Você pode usar um ponteiro duplo para a entrada, como este:

void foobar(char **input, int newlength)
{
   *input = realloc(*input, newlength * sizeof(char));
}

Se o chamador tem um duplicado do ponteiro de entrada em algum lugar, que ainda duplicado poderá ser inválido agora.

Eu acho que a solução mais limpa aqui é evitar o uso de realloc () ao tentar modificar a entrada da função do chamador. Apenas malloc () um novo buffer, devolva aquele, e deixar o chamador decidir se quer ou não para libertar o texto antigo. Isto tem a vantagem de permitir que o chamador manter a string original!

Respondeu 08/08/2008 em 22:37
fonte usuário

votos
11

Como regra geral, você deve não fazer uma livre ou realloc em um buffer de usuário fornecido. Você não sabe onde o usuário alocado o espaço (em seu módulo, em outra DLL) para que você não pode usar qualquer uma das funções de alocação em um buffer do usuário.

Desde que você já não pode fazer qualquer realocação dentro de sua função, você deve mudar seu comportamento um pouco, como fazer apenas uma substituição, para que o usuário será capaz de calcular o comprimento da corda máximo resultante e fornecê-lo com um buffer de tempo suficiente para que este substituição de ocorrer.

Em seguida, você pode criar uma outra função para fazer as várias substituições, mas você terá que alocar todo o espaço para a string resultante e copiar a cadeia de entrada do usuário. Em seguida, você deve fornecer uma maneira de apagar a seqüência que você alocado.

Resultando em:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Respondeu 04/08/2008 em 12:19
fonte usuário

votos
6

Alguém se desculpou por estar atrasado para a festa - dois meses e meio atrás. Oh bem, eu gastar um monte de tempo fazendo arqueologia software.

Estou interessado que ninguém comentou explicitamente sobre o vazamento de memória no projeto original, ou o erro off-por-um. E foi observando o vazamento de memória que me diz exatamente por isso que você está recebendo o erro double-free (porque, para ser mais preciso, você está liberando a mesma memória várias vezes - e você está fazendo isso depois de pisar sobre a memória já libertado).

Antes de realizar a análise, eu concordo com aqueles que dizem a sua interface é menos do que estelar; No entanto, se você lidou com o vazamento de memória / questões pisoteio e documentou a exigência 'devem ser alocados de memória', que poderia ser 'OK'.

Quais são os problemas? Bem, você passar um buffer para realloc () e realloc () retorna um novo ponteiro para a área que você deve usar - e você ignorar esse valor de retorno. Consequentemente, realloc () tem, provavelmente, libertou a memória original, e então você passá-lo o mesmo ponteiro novamente, e ele se queixa de que você está liberando a mesma memória duas vezes, porque você passa o valor original a ele novamente. Isto não só vazamentos de memória, mas significa que você está continuando a usar o espaço original - e remate de John Downey nos pontos escuros que você está fazendo mau uso realloc (), mas não enfatiza quão severamente você está fazendo isso. Há também um erro off-por-um, porque você não alocar espaço suficiente para o NUL '\ 0' que termina a string.

O vazamento de memória ocorre porque você não fornecer um mecanismo para informar o chamador sobre o último valor da cadeia. Porque você manteve pisando sobre a string original mais o espaço depois dela, parece que o código funcionou, mas se o seu código de chamada libertou o espaço, ele também iria obter um erro de double-free, ou pode obter um core dump ou equivalente, porque as informações de controle de memória é completamente mexidos.

Seu código também não protege contra o crescimento indefinido - considere a substituição 'Noel' com 'Joyeux Noel'. Cada vez, você gostaria de acrescentar 7 caracteres, mas você gostaria de encontrar outra Noel no texto substituído, e expandi-lo, e assim por diante e assim por diante. Meu correção (abaixo) não aborda esta questão - a solução simples é, provavelmente, para verificar se a cadeia de pesquisa aparece no texto de substituição; uma alternativa é pular o texto de substituição e continuar a pesquisa após ele. O segundo tem alguns problemas de codificação não-trivial para resolver.

Portanto, a minha revisão sugerido de sua função chamada é:

char *strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while ((find = strstr(find, search)) != 0) {
        if (delta > 0) {
            input = realloc(input, strlen(input) + delta + 1);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input));
        memmove(find, replace, replaceLen);
    }

    return(input);
}

Este código não detecta erros de alocação de memória - e, provavelmente, falhar (mas se não, vazamentos de memória) se realloc () falhar. Veja 'Código Escrevendo sólido' livro de Steve Maguire para uma extensa discussão de questões de gerenciamento de memória.

Respondeu 21/10/2008 em 03:41
fonte usuário

votos
6

Apenas um tiro no escuro, porque eu não tentei ainda, mas quando você realloc ele retorna o ponteiro muito parecido com malloc. Porque realloc pode mover o ponteiro se necessário você provavelmente está operando em um ponteiro inválido se você não faça o seguinte:

input = realloc(input, strlen(input) + delta);
Respondeu 04/08/2008 em 12:14
fonte usuário

votos
4

Nota, tente editar o seu código para se livrar dos códigos html de escape.

Bem, embora tenha sido um tempo desde que eu usei C / C ++, realloc que só cresce reutiliza o valor ponteiro de memória, se houver espaço na memória depois que o seu bloco original.

Por exemplo, considere o seguinte:

(Xxxxxxxxxx ..........)

Se o ponteiro aponta para os primeiros x, e. significa posição de memória livre, e você aumentar o tamanho da memória apontada por sua variável por 5 bytes, ele vai ter sucesso. Isto é, claro, um exemplo simplificado como blocos são arredondados para um determinado tamanho para o alinhamento, mas de qualquer maneira.

No entanto, se você posteriormente tentar crescer por mais 10 bytes, e há apenas 5 disponíveis, será necessário para mover o bloco na memória e atualizar o ponteiro.

No entanto, no seu exemplo você está passando a função um ponteiro para o personagem, não um ponteiro para a variável e, portanto, enquanto a função strRep internamente pode ser capaz de ajustar a variável em uso, é uma variável local para a função strRep e seu código de chamada vai ficar com o valor da variável ponteiro originais.

Este valor de ponteiro, no entanto, foi libertado.

No seu caso, a entrada é o culpado.

No entanto, gostaria de fazer outra sugestão. No seu caso parece que a entrada variável é de fato entrada, e se for, ele não deve ser modificado, em tudo.

Gostaria, assim, tentar encontrar uma outra maneira de fazer o que você quer fazer, sem alterar de entrada , como efeitos colaterais como isso pode ser difícil de rastrear.

Respondeu 04/08/2008 em 12:17
fonte usuário

votos
3

realloc é estranho, complicado e só deve ser usado quando se lida com lotes de lotes de memória de vezes por segundo. ie - onde ele realmente faz seu código mais rápido.

Eu vi código onde

realloc(bytes, smallerSize);

foi usado e trabalhou para redimensionar o buffer, tornando-se menor. Trabalhou cerca de um milhão de vezes, em seguida, por algum motivo realloc decidiu que, mesmo se você estava encurtando o buffer, que lhe daria uma boa nova cópia. Então você falhar em um lugar aleatório 1/2 segundo após o material ruim aconteceu.

Sempre use o valor de retorno de realloc.

Respondeu 16/05/2011 em 23:57
fonte usuário

votos
3

Isso parece funcionar;

char *strrep(char *string, const char *search, const char *replace) {
    char *p = strstr(string, search);

    if (p) {
        int occurrence = p - string;
        int stringlength = strlen(string);
        int searchlength = strlen(search);
        int replacelength = strlen(replace);

        if (replacelength > searchlength) {
            string = (char *) realloc(string, strlen(string) 
                + replacelength - searchlength + 1);
        }

        if (replacelength != searchlength) {
            memmove(string + occurrence + replacelength, 
                        string + occurrence + searchlength, 
                        stringlength - occurrence - searchlength + 1);
        }

        strncpy(string + occurrence, replace, replacelength);
    }

    return string;
}

Suspiro, há de qualquer maneira para postar código sem ele chupar?

Respondeu 04/08/2008 em 12:39
fonte usuário

votos
0

Minhas dicas rápidas.

Em vez de:
void strrep(char *input, char *search, char *replace)
tentar:
void strrep(char *&input, char *search, char *replace)

e que no corpo:
input = realloc(input, strlen(input) + delta);

Geralmente ler sobre a passagem de argumentos da função como valores de referência / e realocar () Descrição :).

Respondeu 04/08/2008 em 12:20
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more