Почему 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 с используемым ранее стилем кодирования.