Всякий раз, когда вы создаете интерфейс для какой-либо функциональности, вам нужно думать об управлении ресурсами - каково время жизни различных объектов и как управляется время жизни этих объектов.Это особенно верно для языков, таких как C и C ++, которые не реализуют сборку мусора, но даже в языках с сборкой мусора, необходимо подумать об этом (для ресурсов, не связанных с памятью, и, возможно, чтобы гарантировать, что ссылки на объекты не хранятсянеопределенно).
В любом случае, для C API могут иметь дело с «выходными» объектами несколькими способами.Вот некоторые из наиболее распространенных шаблонов:
- позволяет вызывающей стороне предоставить буфер или объект для размещения результатов в
- , чтобы вызываемый объект выделял буфер или объект и возвращал указатель навыделенный объект
- заставляет вызывающую функцию возвращать объект непосредственно из функции - это работает только для элементов, имеющих фиксированный размер (внутренний тип или структуру) - это не работает для произвольных строк.Поэтому я не буду обсуждать это дальше.
Для варианта 1 вызывающая сторона предоставляет буфер для размещения результатов, передавая указатель.Для данных переменной длины, таких как строка, важно, чтобы интерфейс также позволял вызывающей стороне передавать размер выходного буфера, чтобы функция могла избежать записи вне буфера.Для вашей функции getFile()
прототип может выглядеть следующим образом:
int getFile(char const* request, char* result, int result_size);
Вызывающая сторона передает указатель для размещения результатов и размера передаваемого в буфер.Функция может вернуть отрицательное число при сбое (скажем, сбой в сети) и размер результата при успехе.Если размер, возвращаемый в случае успеха, больше, чем предоставленный буфер, вызывающая сторона знает, что полный результат не был помещен в буфер (поскольку он недостаточно велик).Если вы идете по этому пути, важно принять во внимание символ-терминатор '\ 0' и четко указать, включен ли он в возвращаемое значение и завершается ли буфер, если результат слишком велик (что я рекомендую).Кроме того, убедитесь, что при передаче буфера нулевого размера функция ничего не записывает в буфер независимо от того, передан ли указатель, отличный от NULL (это обычный шаблон, когда функция возвращает требуемый размербуфер в этом случае).
Для варианта 2 функция будет выделять соответствующий буфер и возвращать указатель на него вызывающей стороне.В этом случае у вызывающей стороны должен быть способ освободить буфер, когда это сделано, чтобы избежать утечки памяти.Одним из способов является документирование того, что вызывающая сторона должна вызвать free()
для указателя, возвращаемого функцией (подразумевая, что функция будет назначать указатель, используя malloc()
или calloc()
).Другой - чтобы интерфейс для использования функции также включал подпрограмму освобождения, возможно, getFile_free()
(я бы определенно переосмыслил названия этих функций, если бы я пошел по этому пути).Это дает реализации этого API свободу распределять буферы, которые он возвращает, как считает нужным - он не ограничен использованием malloc()
/ free()
(хотя это возможно).
Для встроенной системы (особеннонебольшие (возможно, не системы на основе Linux), я думаю, что люди могут выбрать вариант 1. Это дает пользователю API свободу вообще избегать динамического выделения памяти, и для встроенных систем характерно не использовать динамическую память.Кроме того, система, использующая динамическое выделение памяти, может по-прежнему использовать шаблон варианта 1 - она просто требует немного больше работы, но эта работа может быть обернута во что-то, что в точности похоже на вариант 2, так что вы можете получить свой торт и съесть его тоже.