Problem z funkcja free() i malloc()

0

Cześć,
Chciałbym zapytać czy jest możliwe usunięcie tablicy w funkcji a następnie jakby stworzenie jej na nowo ? Próbuje to robić ale całkowicie nie wychodzi, niby usuwam w funkcji wcześniejsze rekordy a ona dalej są na danym adresie. Podsyłam poniżej na czym polega zadanie:

9.2 Napisz funkcję, która zmienia rozmiar tablicy z n-elementowej na m-elementową (nowy wymiar może być mniejszy lub większy od początkowego). Zadanie wymaga stworzenia nowej tablicy i przepisania do niej zawartości starej.

Chciałem zrobić to w ten sposób:

wczytuje tablice array do funkcji, tworzę tablice zastępczą tmp by przechowała mi elementy tablicy, następnie usuwam funkcje array , tworzę ją z powrotem tylko z mniejszą liczbą rekordów, podaną do funkcji i wczytuje elementy do array. Problem w tym, że usuwam pozostałe rekordy tablicy a one nadal są w tablicy.

int* expandArray(int *array, int newSize)
{
    int *nowaTablica = malloc(sizeof(int)*newSize);
    for(int i = 0; i < newSize; i++)
    {
        nowaTablica[i] = array[i];
    }

    free(*array);
    *array = NULL;

        for(int i = 0; i < newSize; i++)
    {
        printf("%d ,", nowaTablica[i]);
    }

    printf("\n");

    *array = malloc(sizeof(int)*newSize);

        for(int i = 0; i < newSize; i++)
    {
        array[i] = nowaTablica[i];
    }
            for(int i = 0; i < newSize; i++)
    {
        printf("%d ,", array[i]);
    }
    return array;
}
1

Po pierwsze zobacz realloc (http://www.cplusplus.com/reference/cstdlib/realloc/)

Po drugie: free(*array); *array = NULL; <- wtf?

1
Łukasz Płoński napisał(a):
free(*array);
*array = malloc(sizeof(int)*newSize);

po co jest * w powyższych instrukcjach?

0

@Miang: Może źle to rozumiem, ale w ten sposób przekazuje podaną do funkcji tablicę, i chce ją wyczyścić a następnie stworzyć na nowo, wskaźnik daje po co by potem zmieniła ona się też w mainie
@stivens : wykładowca stwierdził, że jak damy array = NULL to będzie to bardziej "eleganckie" ;D

0

Ok, poprawiłem te wskaźniki. Teraz w środku funkcji czyści elementy tablicy bo tam jakoś kulawo to testowałem. Ale nadal gdy tworzę tą tablicę na nowo to jakby z automatu robi jej rozmiar 5 (gdy wywołuje array[3] to normalnie ją wyświetla i to taką samą wartość jaką miała a nie powinna wyświetlać) a ja podaje rozmiar 3...

w ten sposób wywołuję tą funkcję:

expandArray(array, 3);

int expandArray(int *array, int newSize)
{
    int *nowaTablica = malloc(sizeof(int)*newSize);
    for(int i = 0; i < newSize; i++)
    {
        nowaTablica[i] = array[i];
    }

    free(array);
    array = NULL;

    array = malloc(sizeof(int)*newSize);

        for(int i = 0; i < newSize; i++)
    {
        array[i] = nowaTablica[i];
    }

    return array;
}

0

Primo: masz mem-leaka (gdzie free na tymczasowej tablicy)?

Duo: Moglbys po prostu zwrocic "tymczasowa" tablice zamiast kopiowac...

Tres: Przeczytales link, ktory Ci dalem? Realloc?


I teraz dlaczego array[3] dziala... Jeny to jest kwestia architektury komputera. A bardziej przydzialu pamieci.

Kurde tutaj by sie jakis wiekszy wpis blogowy przydal zeby Ci wytlumaczyc a nie tylko chaos wprowadzic.

Ale z grubsza malloc nie przydziela dokladnie "x bajtow" ale jakis wiekszy, ciagly obszar pamieci. I jak zamowisz np. 50 bajtow to mozesz dostac 64 (albo wiecej ale prawie na pewno nie mniej). Malo tego - generalnie dostepy do calej strony pamieci (4 kiB?) nie zakoncza sie segfaultem bo ta strona jest jakby cala do dyspozycji procesu. Ale jesli bedziesz to exploitowac to malloc moze zwrocic adres pamieci, ktory juz jest niewlasciwie wykorzystywany.

Tutaj sie akurat tak wydarza, ze po zwolnieniu pamieci, pytajac ponownie o jakis blok dostajesz dokladnie ten sam obszar ktory zwolniles. No i nie jest on wyzerowany.

Zaimplementowanie (przeczytanie implementacji/slajdow na ten temat) malloca duzo moze rozjasnic. Ale poki co nie umiesz sie jeszcze wskaznikami poslugiwac wiec to chyba za duze wyzwanie.

0

@Łukasz Płoński: najlepiej to pokaz mi jeszcze kod w ktorym wykonujesz expand bo mam pewne podejrzenia...

0

@stivens:

int main ()
 {

    int size = 10, rows = 4, cols = 5;
    srand(time(NULL));

    int *arr = createArray(size);

    int **matrix = createMatrix(rows, cols);

    int *array = createArray(5);
    showArray(array, 5);
    expandArray(array, 3);


    deleteArray(&array);
    deleteArray(&arr);
    return 0;
 }
0

No i w ogole nie przechwytujesz nowego wskaznika.

Jesli w ten sposob testowales to byc moze wcale nie dostajesz tego samego obszaru pamieci a zwyczajnie robisz illegal use after free. (pamiec po zwolnieniu nie jest czyszczona)

EDIT:

    expandArray(array, 3);

    deleteArray(&array)

To jest totalnie zle.

EDIT2:
Jest nawet jeszcze gorzej... &array ma typ int** i nie wskazuje na tablice tylko na adres jej adresu. (w tym przypadku na stos?)

0
void deleteArray(int **ar)
{

    free(*ar);
    *ar = NULL;

}
1

Aha. Kod jest tak napisany, ze jest de facto poprawny ale w <cross>opor patologiczny.</cross>

EDIT: chociaz moze dostrzegam w nim sens bo faktycznie wskaznika juz sie uzyc nie da :)

1
void expandArray(int **array,int newSize)
{
    *array=(int*)realloc(*array,sizeof(int)*newSize);
}

W sumie radzę przejść do działań na strukturze:

typedef struct { size_t size; int *data; } ArrayInt1;

wtedy:

void Init(ArrayInt1 *array);
void Resize(ArrayInt1 *array,size_t newsize);
void Free(ArrayInt1 *array);
void Show(ArrayInt1 *array);
void Input(ArrayInt1 *array);
void Create(ArrayInt1 *array,size_t size) { Init(array); Resize(array,size); }
void CreateAndInput(ArrayInt1 *array,size_t size) { Create(array,size); Input(array); }
0

Zrobiłem tak, że działa ale podejrzewam, że mimo wszystko jest to źle zrobione:
wywołanie:

expandArray(array, 3, &array);

funkcja:

int expandArray(int *array, int newSize, int **arr)
{
    int *nowaTablica = malloc(sizeof(int)*newSize);
    for(int i = 0; i < newSize; i++)
    {
        nowaTablica[i] = array[i];
    }

    free(*arr);
    *arr = NULL;



    array = malloc(sizeof(int)*newSize);

        for(int i = 0; i < newSize; i++)
    {
        array[i] = nowaTablica[i];
    }

    return array;
}
0

Wywal wypisywanie do osobnej funkcji i nie wywołuj jej tu.
Zamiast pętli użyj memcpy().
Po kiego ci dwa kopiowania?

1

Typ zwracany funkcji: int
A co zwracasz? array

Po co ci array i &array oddzielnie?

EDIT: w ogole to nalegam aby program kompilowac z flagami -Wall Wextra i czytac ostrzezenia...

1

Przepraszam, czy tylko mi przeszkadza to, że expandArray zna nowy rozmiar, ale nie zna starego? W szczególności jeśli nowy rozmiar będzie od starego większy o co najmniej 1024 elementy to masz spore szanse na SIGSEGV (odczyt z niezaalokowanej strony).
Przy okazji, zainstaluj sobie valgrinda i zacznij sprawdzać:

valgrind --leak-check=full --show-leak-kinds=all ./plik_wykonywalny

to się dowiesz sporo o potencjalnym niedziałaniu programu. Albo, alternatywnie (alternatywnie, bo obu metod naraz używać nie można), skompiluj program o tak:

gcc -Wall -Wextra program.c -o plik_wykonywalny -fsanitize=undefined,address

to bardzo ładnie się przekonasz o jakichkolwiek (może to jest drobna przesada) błędach dostępu do pamięci.

0

Jeżeli się upierasz robić bez struktur to powinno to wyglądać następująco:

void resizeArray(int **array,size_t oldSize,size_t newSize,int value)
{
    int *newArray=(int*)malloc(sizeof(int)*newSize);
    size_t minSize=min(oldSize,newSize);
    memcpy(newArray,*array,sizeof(int)*minSize);
    for(size_t i=minSize;i<newSize;++i) newArray[i]=value;
    free(*array);
    *array=newArray;
}
0

Temat do zamknięcia, dziękuje wszystkim za chęć pomocy

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