Twoi profesorowie podnoszą ważną kwestię. Niestety użycie języka angielskiego jest takie, że nie jestem absolutnie pewien, co zostało powiedziane. Pozwól, że odpowiem na to pytanie w kategoriach programów niebędących zabawkami, które mają pewne cechy wykorzystania pamięci iz którymi osobiście pracowałem.
Niektóre programy dobrze się zachowują. Alokują pamięć falami: wiele małych lub średnich alokacji, po których następuje wiele zwolnień, w powtarzających się cyklach. W tych programach typowe alokatory pamięci radzą sobie raczej dobrze. Łączą uwolnione bloki i pod koniec fali większość wolnej pamięci jest w dużych, ciągłych fragmentach. Te programy są dość rzadkie.
Większość programów źle się zachowuje. Alokują i zwalniają pamięć mniej lub bardziej losowo, w różnych rozmiarach, od bardzo małych do bardzo dużych, i zachowują wysokie wykorzystanie przydzielonych bloków. W programach tych zdolność łączenia bloków jest ograniczona iz czasem kończą się one dużą fragmentacją pamięci i względnie nieciągłością. Jeśli całkowite użycie pamięci przekracza około 1,5 GB w 32-bitowej przestrzeni pamięci, a alokacje wynoszą (powiedzmy) 10 MB lub więcej, w końcu jeden z dużych przydziałów zakończy się niepowodzeniem. Te programy są powszechne.
Inne programy zwalniają niewiele pamięci lub nie zajmują jej wcale, dopóki się nie zatrzymają. Stopniowo alokują pamięć podczas pracy, zwalniając tylko małe ilości, a następnie zatrzymują się, po czym cała pamięć jest zwalniana. Kompilator jest taki. Tak jest z maszyną wirtualną. Na przykład środowisko wykonawcze .NET CLR, samo napisane w C ++, prawdopodobnie nigdy nie zwalnia pamięci. Dlaczego miałoby to robić?
I to jest ostateczna odpowiedź. W przypadkach, w których program zajmuje wystarczająco dużo pamięci, zarządzanie pamięcią za pomocą malloc i free nie jest wystarczającą odpowiedzią na problem. O ile nie masz szczęścia, aby mieć do czynienia z dobrze działającym programem, będziesz musiał zaprojektować jeden lub więcej niestandardowych alokatorów pamięci, które wstępnie alokują duże fragmenty pamięci, a następnie przydzielają podrzędnie zgodnie z wybraną strategią. Nie możesz w ogóle używać darmowego, z wyjątkiem sytuacji, gdy program się zatrzyma.
Nie wiedząc dokładnie, co powiedzieli twoi profesorowie, w przypadku programów o prawdziwie produkcyjnej skali prawdopodobnie stanąłbym po ich stronie.
EDYTOWAĆ
Spróbuję odpowiedzieć na niektóre uwagi krytyczne. Oczywiście SO nie jest dobrym miejscem na tego typu posty. Dla jasności: mam około 30 lat doświadczenia w pisaniu tego rodzaju oprogramowania, w tym kilku kompilatorów. Nie mam żadnych akademickich odniesień, tylko własne siniaki. Nie mogę oprzeć się wrażeniu, że krytyka pochodzi od ludzi z dużo węższym i krótszym doświadczeniem.
Powtórzę mój kluczowy komunikat: zbalansowanie malloc i free nie jest wystarczającym rozwiązaniem do alokacji pamięci na dużą skalę w rzeczywistych programach. Blok koalescencyjny jest normalne, i kupuje czas, ale to nie wystarczy. Potrzebujesz poważnych, sprytnych alokatorów pamięci, które mają tendencję do przechwytywania pamięci w fragmentach (za pomocą malloc lub cokolwiek innego) i rzadko zwalniają. To prawdopodobnie przesłanie, które mieli na myśli profesorowie OP, które źle zrozumiał.