реконструированный вариант программы, свободный от утечек
Что изменилось? Абсолютно все! Теперь для внепланового выхода из программы (который осуществляется по break), нам уже не нужно помнить, что мы успели выделить или открыть! По завершении цикла while (который на самом деле никакой не цикл, а просто имитация критикуемого оператора goto), мы освобождаем (или точнее, пытаемся освободить) _все_ ресурсы, которые потенциально могли быть выделены. Структура программы значительно упрощается и главное тут — не забыть освободить все, что мы выделили, но программисты об этом все равно забывают.
Решение заключается в создании своей собственной обертки вокруг функции malloc
(условно назовем ее my_malloc), которая выделят память, запоминает указатель в своем списке/массиве и перед возвращением в вызываемую функцию подменяет адрес возврата из материнский функции на свой собственный обработчик (конечно, без ассемблерных вставок тут не обойтись, но они того стоят). Как следствие — при выходе из foo, управление получает обработчик my_malloc, читающий список/массив и автоматически освобождающий все, что там есть, снимая тем самым эту ношу с плеч программиста.
Если же выделенная функцией память по замыслу разработчика не должна освобождаться после ее завершения, на этот случай можно предусмотреть специальный флаг, передаваемый my_malloc, и сообщающий, что этот блок освобождать не надо и программист освободит его сам.
Одним их самых мерзких недостатков языка Си является отсутствие поддержки динамических стековых массивов. Стековая память хороша тем, что автоматически освобождается при выходе из функции, однако, выделение стековых массивов "на лету" невозможно и мы должны заранее объявить их размеры при объявлении переменных. В C99 сделаны небольшие подвижки в этом направлении и теперь мы можем объявлять массивы, размер которых задается аргументом, передаваемым функции, однако, это не решает всех проблем и к тому же C99 поддерживают далеко не все компиляторы.
В частности, компилятор GCC 2.95 нормально "переваривает" следующий код, а Microsoft Visual C++ увы, нет:
f(int n)
{
char buf[n];
return sizeof(buf);
}