указатель как второй аргумент вместо возврата указателя?
Я заметил, что это распространенная идиома в C, чтобы принять un -malloc
указатель ed в качестве второго аргумента вместо возврата указателя. Пример:
/*function prototype*/
void create_node(node_t* new_node, void* _val, int _type);
/* implementation */
node_t* n;
create_node(n, &someint, INT)
вместо
/* function prototype */
node_t* create_node(void* _val, int _type)
/* implementation */
node_t* n = create_node(&someint, INT)
каковы преимущества и/или недостатки обоих подходов?
спасибо!
редактировать спасибо всем за ваши ответы. Мотивы выбора 1 теперь мне очень ясны (и я должен указать, что аргумент указателя для выбора 1 должно быть Танос бы противоречит тому, что я изначально думал).
7 ответов
принимать указатель (который вызывающий абонент отвечает за malloc'ing или нет) на память, которая будет заполнена, предлагает серьезные преимущества в гибкости над возвращение указатель (обязательно Танос объед). В частности, если вызывающий объект знает, что ему нужно использовать все, что возвращается только в определенной функции, он может передать адрес структуры или массива, выделенного стеком; если он знает, что ему не нужно повторное обращение, он может передать адрес static
struct или массив -- в любом случае, пара malloc/free сохраняется, и такие сбережения действительно монтируются!-)
это не имеет большого смысла. Указатели в C передаются по значению, как и другие объекты-разница заключается в значении. С помощью указателей значением является адрес памяти, который передается функции. Тем не менее, вы все еще дублируете значение, и поэтому, когда вы malloc
, вы будете изменять значение указателя внутри функции, а не снаружи.
void create_node(node_t* new_node, void* _val, int _type) {
new_node = malloc(sizeof(node_t) * SIZE);
// `new_node` points to the new location, but `n` doesn't.
...
}
int main() {
...
node_t* n = NULL;
create_node(n, &someint, INT);
// `n` is still NULL
...
}
есть три способа избежать этого. Во-первых, как вы упомянули, возвращение нового указатель от функции. Второй-взять указатель на указатель, тем самым передав его по ссылке:
void create_node(node_t** new_node, void* _val, int _type) {
*new_node = malloc(sizeof(node_t) * SIZE);
// `*new_node` points to the new location, as does `n`.
...
}
int main() {
...
node_t* n = NULL;
create_node(&n, &someint, INT);
// `n` points to the new location
...
}
третий - просто malloc
n
вне вызова функции:
int main() {
...
node_t* n = malloc(sizeof(node_t) * SIZE);
create_node(n, &someint, INT);
...
}
Я обычно предпочитаю получать указатели (инициализированное свойство) в качестве аргументов функции, а не возвращать указатель на область памяти, которая была malloc'Ed внутри функции. При таком подходе вы делаете явным, что ответственность за управление памятью лежит на стороне пользователя.
возврат указателей обычно приводит к утечкам памяти, так как проще забыть освободить() ваши указатели, если вы не malloc()'ed их.
обычно я не делаю фиксированного выбора, я аккуратно помещаю его в свою собственную библиотеку и предоставляю лучшее из обоих миров.
void node_init (node_t *n);
void node_term (node_t *n);
node_t *node_create ()
{
node_t *n = malloc(sizeof *n);
/* boilerplate error handling for malloc returning NULL goes here */
node_init(n);
return n;
}
void node_destroy (node_t *n)
{
node_term(n);
free(n);
}
для каждого malloc должен быть свободный, таким образом, для каждого init должен быть термин, и для каждого создания должно быть разрушение. По мере того, как ваши объекты становятся все более сложными, вы обнаружите, что начинаете их гнездить. Некоторые объекты более высокого уровня могут использовать список node_t для внутреннего управления данными. Прежде чем освободить этот объект, необходимо сначала освободить список. _init и _term заботятся об этом, полностью скрывая эту деталь реализации.
могут быть решения о дальнейших деталях, например, destroy может принять node_t * * n и установить *n в NULL после его освобождения.
лично мне нравится возвращать данные с помощью параметров refernce или pointer и использовать функцию return для возврата кодов ошибок.
1) Как указал Самир, код неверен, указатель передается по значению, вам нужно **
2) функция по существу является конструктором, поэтому имеет смысл как выделить память, так и ввести структуру данных. Чистый код C почти всегда ориентирован на объект, как это с конструкторами и деструкторами.
3) ваша функция void, но она должна возвращать int, чтобы она могла возвращать ошибки. Будет как минимум 2, возможно 3 возможных ошибки условия: malloc может завершиться ошибкой, аргумент типа может быть недопустимым и, возможно, значение может быть вне диапазона.
проблема, не обсуждаемая в этой статье, заключается в том, как вы ссылаетесь на буфер malloc'ed в функции, которая выделяет его и, по-видимому, сохраняет что-то в нем, прежде чем он возвращает управление вызывающему объекту.
в случае, который привел меня на эту страницу, у меня есть функция, которая передает указатель, который получает адрес массива структур HOTKEY_STATE. Прототип объявляет аргумент следующим образом.
HOTKEY_STATE ** plplpHotKeyStates
возвращаемое значение, ruintNKeys - это количество элементов в массиве, которое определяется подпрограммой до выделения буфера. Однако вместо того, чтобы использовать malloc() напрямую, я использовал calloc следующим образом.
*plplpHotKeyStates = ( HOTKEY_STATE * ) calloc ( ruintNKeys ,
sizeof ( HOTKEY_STATE ) ) ;
после проверки того, что plplpHotKeyStates больше не null, я определил локальную переменную указателя, hkHotKeyStates, следующим образом.
HOTKEY_STATE * hkHotKeyStates = *plplpHotKeyStates ;
используя эту переменную с целым числом без знака для индекса, код заполняет структуры, используя простой оператор-член (.), как показано ниже.
hkHotKeyStates [ uintCurrKey ].ScanCode = SCANCODE_KEY_ALT ;
когда массив полностью заполнен, он возвращает ruintNKeys, и вызывающий объект имеет все необходимое для обработки массива, либо обычным способом, используя ссылочный оператор ( - > ), либо используя тот же метод, который я использовал в функции для получения прямого доступа к массиву.