Почему C-код не возвращает структуру?
В то время как это очень удобно, я очень редко встречаю функции, возвращающие struct
(или union
s) в C, независимо от того, являются ли они динамически связанными функциями или статически определенными функциями.
Вместо этого они возвращают данные через параметр указателя.
(Динамический пример в Windows - GetSystemInfo
.)
В чем причина этого?
Это из-за проблемы с производительностью, проблемы совместимости с ABI или чего-то еще?
Ответы
Ответ 1
Я бы сказал "производительность", плюс тот факт, что даже иногда это может удивить программистов C. Это не... в общем "аромате" C, для многих, чтобы бросить вокруг больших вещей, таких как структуры, как если бы они были просто ценностями. Который, по языку, они действительно есть.
В то же время многие программисты C, похоже, автоматически прибегают к memcpy()
, когда возникает необходимость копировать структуры, а не просто использовать назначение.
В С++, по крайней мере, есть что-то, называемое "оптимизацией возвращаемого значения", которое позволяет безшовно преобразовать код следующим образом:
struct Point { int x, y; };
struct Point point_new(int x, int y)
{
struct Point p;
p.x = x;
p.y = y;
return p;
}
в
void point_new(struct Point *return_value, int x, int y)
{
struct Point p;
p.x = x;
p.y = y;
*return_value = p;
}
который устраняет ( "потенциально стек" ) "истинный" возврат значения структуры. Я думаю, еще лучше было бы это, не уверен, что они такие умные:
void point_new(struct Point *return_value, int x, int y)
{
return_value->x = x;
return_value->y = y;
}
Я не уверен, могут ли компиляторы C сделать что-либо из этого, если они не могут, то я думаю, что это может быть реальным аргументом против возврата структуры для очень важных для производительности программ.
Ответ 2
Возврат в C производится путем сохранения возвращаемого значения в stack.
Возврат структуры или объединения приведет к тому, что потенциально очень большие данные в стеке могут привести к переполнению стека .
Возвращение только указателя на struct/union в значительной степени безопаснее, потому что вы кладете только небольшое количество данных (всего 4 байта) в стеке.
Ответ 3
Причины в основном исторические. В своей статье "Текстовый редактор sam
" , Роб Пайк пишет
Связанный вопрос стиля программирования: sam
часто передает структуры по значению, что упрощает код. Традиционно программы C передают структуры по ссылке, но неявное выделение в стеке проще в использовании. Прохождение структуры является относительно новой особенностью C (она не находится в стандартном справочном руководстве для C 14) и плохо поддерживается в большинстве коммерческих компиляторов C. Его удобная и выразительная, хотя и упрощает управление памятью, полностью избегая использования распределителя и устраняя псевдонимы указателей.
Таким образом, есть недостатки в технике; возвращая неприлично большие структуры, вероятность.
Ответ 4
Функция C C может возвращать структуру (а также С++, где она довольно распространена). Возможно, в первые годы существования C он не мог.
x86-64 Спецификация ABI для Linux и связанных с ним систем (стр. 21) даже говорит о том, что структуры, соответствующие двум -64 битным словам часто могут быть возвращены в два регистра, не переходя через память (даже стек). Вероятно, это быстрее, чем через стек.
Как он ответил в своем ответе, ABI часто требует, чтобы результаты структуры были молча преобразованы в невидимый указатель.
Можно даже определить другое соглашение о вызове, которое возвращает больше данных в большем количестве регистров. Но такие новые соглашения разрушили бы весь объектный код и потребовали бы перекомпиляции всего (включая даже Linux, системные библиотеки, такие как libc.so.6
), и, конечно же, потребовать изменения компилятора.
Конечно, соглашения ABI связаны с процессором, системой и компилятором.
Я не знаю Windows, и я не знаю, что Windows определяет как ее ABI.
Ответ 5
В pre-ANSI C вы не можете вернуть объект типа структуры, и вы также не можете передавать аргументы типов структуры.
Из цитаты Криса Торека в comp.lang.c:
Обратите внимание, что V6 C также не поддерживает структурнозначные аргументы и struct-value возвращаемые значения.
В наши дни они не очень популярны, поэтому люди предпочитают возвращать указатель на структуру, которая включает только копию указателя вместо копии всего структурного объекта.
Ответ 6
В дополнение к идее о том, что может произойти сбой производительности или что возвращаемые структуры по значению, возможно, не были обычно поддерживаются в предстандартные дни, другая причина, по которой функции C не использовать return-by-value для structs, заключается в том, что если вы возвращаете структуру, которую вы не можете легко вернуть индикатор успеха/сбоя. Я знаю, что иногда я начинаю разрабатывать функцию, чтобы вернуть структуру, которая была инициализирована в функции, но затем я столкнулся с проблемой того, как указать, была ли эта функция успешной. У вас довольно много вариантов:
- гарантировать успех (иногда это возможно)
- передать указатель на местоположение для кода ошибки
- имеют значение поля или дозорности в структуре, которое указывает на успех/отказ
Только опция 1 не позволяет интерфейсу быть кликом. Второй вариант разногласий ставит целью вернуть структуру по значению и фактически затрудняет использование функции для обработки сбоев. Третий вариант практически не является хорошим дизайном почти во всех случаях.
Ответ 7
В общем, Windows функционирует либо не возвращает ничего, либо коды ошибок, особенно когда дело доходит до возвращающихся структур или классов.
Эффективность может быть проблемой, хотя RVO должен удалить служебные данные.
Я думаю, что основная причина заключалась в том, чтобы поддерживать методы inline с используемым ранее стилем кодирования.