Zwalnianie pamieci tablicy char free()

0

Witam.
Napisalem (probowalem) napisac funkcja sklejajaca wyrazy w zdanie, dynamicznie alokujaca pamiec. Wszystko wydaje sie działać, jednak po ponownym uruchomieniu tej samej funkcji (drugi raz w tym samym wywołaniu programu) w zmiennej result pozostaje wynik z poprzedniego wywołania funkcji.. Nie rozumiem dlaczego skoro zwalniam pamięć przed wyjsciem z funkcji.

void Cat(){    
    char **words, *tmp_w, *result;
    int len=10, i=0, j=0,x=0, num=0, ch;
    
    printf("Ile wyrazow chcesz wczytac?: ");
    scanf("%d",&num);
    getchar();
    /*Poczatkowa alokacja pamieci*/
    words=(char**)malloc(num*(sizeof(char*)));
    result=(char*)malloc(num*len*sizeof(char));

    for(i=0;i<num;i++){
        words[i]=(char*)(malloc(len*sizeof(char)+1));
        if (words[i]==NULL) exit(1);
    }

    /*Wczytywanie wyrazow*/
    for(i=0;i<num;i++){
        j=0;
        printf("Wyraz [%d]: ",i);
        while((ch=getchar())!='\n'){
            /*Realokacja pamieci w wypadku gdy wyraz jest za dlugi*/
            if(j > len-1){
                len*=2;
                if((tmp_w=(char*)(realloc(result,num*len*sizeof(char))))==NULL) 
                 exit(1);
                result=tmp_w;
                for(x=0;x<num;x++){
                if((tmp_w=(char*)(realloc(words[x],len*sizeof(char))))==NULL){
                    printf("Blad realokacji pamieci!\n");
                    for(i=0;i<num;i++){
                    free(words[i]);
                    }
                    free(words);
                    free(result);
                    break;
                }
                else
                    words[x]=tmp_w;              
                }
            }
            words[i][j++]=ch;
        }
        words[i][j]=0;
    }  
    /*Polaczanie wyrazow w zdanie*/
    for(i=0;i<num;i++){
        strcat(result,words[i]);
        strcat(result," ");
    }
       
    printf("\nZdanie: %s",result);

    /*Zwalnianie pamieci */
    for(i=0;i<num;i++){
        free(words[i]);
    }
    free(words);
    free(result);
    getchar();
} 
0

Pokusiłem się o programik takim jak ja go widzę:

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

#define INITIAL_STRING_BUFF_SIZE 8

typedef struct
{
    char *buff;
    unsigned buff_size;
    unsigned length;
}
string_t;

unsigned max_u(unsigned a, unsigned b)
{
    return (a > b) ? a : b;
}

void StringCreate(string_t *str)
{
    memset(str, 0, sizeof(string_t));
}

void StringDestroy(string_t *str)
{
    free(str->buff);
}

void StringReserve(string_t *str, unsigned required)
{
    if (required < str->buff_size) return;

    required++; // +1 for null-terminator
    str->buff_size = max_u(INITIAL_STRING_BUFF_SIZE, max_u(required, str->buff_size * 2));
    str->buff = (char*)realloc(str->buff, str->buff_size);
}

void StringAppendChar(string_t *str, char to_add)
{
    StringReserve(str, str->length + 1);
    str->buff[str->length++] = to_add;
}

void StringAppendCStr(string_t *str, char *to_add)
{
    while (*to_add != '\0') StringAppendChar(str, *to_add++);
}

char *StringGetCStr(string_t *str)
{
    str->buff[str->length] = '\0';
    return str->buff;
}

#define MAX_WORD_LENGTH 79
#define WORD_SCAN_FMT "%79s"

int main()
{
    string_t sentence;
    char word[MAX_WORD_LENGTH + 1];
    unsigned i, num_words;

    StringCreate(&sentence);

    printf("Ile wyrazow chcesz wczytac?: ");
    scanf("%d", &num_words);
 
    for (i = 0; i < num_words; i++) {
        printf("Wyraz [%d]: ", i);
        scanf(WORD_SCAN_FMT, word);
        StringAppendCStr(&sentence, word);
        StringAppendChar(&sentence, ' ');
    }

    printf("Zdanie: %s", StringGetCStr(&sentence));
    StringDestroy(&sentence);
}
0

Chciałem sie raczej dowiedziec co stwarza problem w moim programie. :P

0

No właśnie poniekąd problem stwarza zła organizacja kodu. A co konkretnie? Ciężko się doszukać ... z powodu złej organizacji kodu.

0

Tak jak napisałem, funkcja działa tylko raz. Gdy uruchomię ja po raz drugi to wydaje sie że nie zwalnia pamięci, ponieważ w komórkach wskazywanych przez result pozostaja wyrazy z poprzedniego uruchomienia. Kiedy wpisuje nowe wyrazy do doklejenia powstałe zdanie składa się ze zdania z poprzedniego uruchomienia + zdania z aktualnego uruchomienia .. Myslałem ze tyle wystarczy aby zwolnić pamięć po zakończeniu funkcji i przygotować ją np. na kolejne uruchomienie.

    /*Zwalnianie pamieci */
    for(i=0;i<num;i++){
        free(words[i]);
    }
    free(words);
    free(result);
0

Ale dlaczego Ty się odwołujesz do zwolnionego obszaru pamięci? Jak wywołasz free na wskaźniku, to staje się on nieprawidłowy i nie powinieneś nic z niego odczytywać. Tu jest chyba Twój problem. A co się dzieje? Po dealokacji zawartość zwolnionego bloku pamięci nie jest w żaden sposób czyszczona i zostają tam stare "śmieci". Jeśli użyjesz go ponownie kiedy po kolejnej alokacji dostaniesz ten sam blok pamięci, to musisz o tym pamiętać i albo zerować pamięć (memset, calloc), albo dbać o wstawienie bajtu zerowego gdzie trzeba. Jeśli tego nie zrobisz, to w wyniku możesz otrzymać nowy c-string połączony ze starym, bo funkcje będą święcie przekonane, że kończy się on na starym bajcie zerowym.

0

Nic z niego nie odczytuje. Po skorzystanie z free wracamy do głownego programu, pozniej jezeli ewentualnie drugi raz uruchomimy ta funkcje powstaja wyzej opisane bledy. :P Memset zadziałał, tyle ze wypełnilem cały zaalokowany obszar '0' co nie wydaje mi sie zbyt optymalne.. Wolałbym pobawić sie wstwianiem 0 tylko w konkretnym miejscu, tylko pytanie którym.. Na końcu kazdego bloku?

0

Zerowanie całego bloku stosuje się w przypadkach, kiedy nie wiesz jakiej długości będzie ciąg wynikowy, czy np. kiedy wstawiasz dane losowo. W Twoim przypadku raczej wiadomo gdzie ciąg się kończy, więc nie ma potrzeby zerowania całego bloku. W którym miejscu masz wstawiać? To chyba oczywiste. Każdy c-string (ciąg znaków) musi być zakończony bajtem zerowym. Dzięki niemu funkcje wiedzą kiedy mają zakończyć przetwarzanie ciągu (printf np. jest jedną z takich funkcji). Logiczne zatem, że masz ten terminator umieścić za ostatnim znakiem ciągu. Właśnie dlatego w words[i]=(char*)(malloc(len*sizeof(char)+1)); jest to ''+1" (nie chcesz mi chyba powiedzieć, że przeklepałeś ten fragment skądś bez wiedzy co to robi i jak działa).

0

Noo własnie dlatego dodałem +1, ale myślałem ze znak \0 ustawiany jest automatycznie na koncu przez funkcje które korzystają ze stringa (scanf chyba tak robi..).. Wydaje mi sie ze na końcu result powinien byc terminator skoro funkcja strcat działa w ten sposób ze dokleja drugi wyraz zastepujac ostatni znak ('\0') pierwszego:

napis1\0 + napis2\0 --> otrzymujemy napis1napis2\0

0

Normalnie funkcje tego typu o to dbają. Jeśli u Ciebie pojawiają się jakieś stare śmieci, to prawdopodobnie z jakiegoś powodu ten bajt zerowy się nie pojawia tam, gdzie powinien. Dlaczego? Ciężko stwierdzić, bo masz straszny bałagan w tym kodzie. Wiesz czego szukać, debuguj.

1 użytkowników online, w tym zalogowanych: 0, gości: 1