openlang

Если присвоить результат realloc единственному указателю, при сбое память утечёт

В функциях, которые увеличивают буфер, часто встречается строка *buffer = realloc(*buffer, n), и ошибка в ней вполне конкретная. Если выделение памяти не удалось, единственный владеющий указатель затирается нулём. По черновику C11 старый объект в этом случае остаётся выделенным и неизменным. Функция теряет единственный путь к памяти, которую ей ещё предстоит освободить.

Функция, о которой идёт речь, получает от вызывающего char **buffer и size_t *capacity. *buffer владеет корректным выделением, а запрошенный размер строго положителен и больше текущей ёмкости.

Результат сначала нужно получить во временный указатель. При неудаче функция сообщает об ошибке выделения и оставляет оба выходных параметра как были. При успехе она записывает возвращённый указатель и новую ёмкость.

В документацию стоит добавить ещё одно правило. Успешный realloc завершает время жизни старого объекта, поэтому сохранённые вызывающим псевдонимы недействительны, даже если возвращённый адрес совпал. Сейчас в черновике функция возвращает только успех или неудачу и требует пересоздавать псевдонимы после успеха. Думаю, этого достаточно, но не уверен, что правило о псевдонимах в нынешней формулировке достаточно ясно.

Разговор продолжается 2

Следуя доводам
  1. Ссылка на ответ

    Для вызывающего кода на C правило о пересоздании псевдонимов годится. Но для скрипта на Lua мне бы хотелось, чтобы пересоздавать было нечего. Память остаётся у хоста на C. Скрипт просит увеличить буфер. Задержимся на этой передаче управления. Обратно приходит либо отказ, и тогда старый буфер на месте и можно попробовать снова, либо новое представление замены, с которым скрипт и работает дальше. Если обёртка вообще не выдаёт сырых адресов, скрипту не нужно помнить, какой сохранённый указатель только что устарел.

  2. Ссылка на ответ

    С RESIZE в Forth такой ошибки не бывает. Он оставляет на стеке новый адрес и ior, а при отказе возвращаемый адрес совпадает со старым, и старая область не тронута. Так что после отказа у тебя тот же указатель, что был, плюс ненулевой код. Ошибиться в присваивании негде, есть только две ячейки и то, что ты с ними сделаешь.

    С псевдонимами Forth свободнее. Стандарт говорит только, что новый адрес может совпасть со старым, а может и нет. Ничего похожего на правило C, что старый объект кончился даже при совпадении адреса, так что твоя строчка в документации строже всего, чего требует Forth.

Другие стороны того же вопроса

  1. Что переживает перезагрузку каталога?

    Срез состоит из адреса и длины. Ни то ни другое не продлевает жизнь выделенной памяти, и справочник Zig 0.15.2 прямо говорит,…