Почему код на C не возвращает структуру? - PullRequest
18 голосов
/ 04 января 2012

Хотя это очень удобно, я очень редко, если вообще когда-либо, сталкиваюсь с функциями, которые возвращают struct s (или union s) в C, будь то динамически связанные функции или статически определенные функции.
Вместо этого они возвращают данные через параметр указателя.

(Динамический пример в Windows - GetSystemInfo.)

В чем причина этого?
Это из-за проблемы с производительностью, проблемы совместимости ABI или чего-то еще?

Ответы [ 7 ]

12 голосов
/ 04 января 2012

Я бы сказал, «производительность», плюс тот факт, что это даже возможно, иногда удивляет программистов на Си.Это не ... в общем "аромате" C, для многих, разбрасывать большие вещи, такие как структуры, как если бы они были просто ценностями.Которые, в зависимости от языка, на самом деле таковы.

В то же время многие программисты на Си, похоже, автоматически прибегают к memcpy(), когда возникает необходимость копировать структуры, а не просто использовать присваивание.

По крайней мере, в C ++ есть нечто, называемое «оптимизацией возвращаемого значения», которое способно беззвучно преобразовывать код следующим образом:

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 могут сделать это, если они не могут, то я думаю, что это можетбыть реальным аргументом против структурных возвратов для очень критичных к производительности программ.

8 голосов
/ 04 января 2012

Причины в основном исторические. В своей статье «Текстовый редактор sam» Роб Пайк пишет

Схожий вопрос стиля программирования: sam часто передает структуры по значению, что упрощает код. Традиционно, C-программы передают структуры по ссылке, но неявное распределение в стеке проще в использовании. Передача структуры является относительно новой функцией C (ее нет в стандартном справочном руководстве для C 14 ), и она плохо поддерживается в большинстве коммерческих компиляторов Си. Однако он удобен и выразителен и упрощает управление памятью, полностью исключая распределитель и исключая псевдонимы указателей.

Как говорится, в технике есть подводные камни; возвращение непристойно больших структур, вероятность переполнения стека.

7 голосов
/ 04 января 2012

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

Возвращение только указателя на структуруобъединение намного безопаснее, поскольку в стек помещается только небольшое количество данных (всего 4 байта).

6 голосов
/ 04 января 2012

Функция AC может возвращать структуру (как и C ++, где она довольно распространена).Возможно, в самые первые годы существования C он не мог этого сделать.

В спецификации x86-64 ABI для Linux и связанных с ней систем (стр. 21) даже говорится, что структуры, умещающиеся в два -64 бита- слова часто могут быть возвращены в двух регистрах, не поступая в память (даже в стек).Очень вероятно, что это быстрее, чем проходить по стеку.

Поскольку unwind ответил в его ответ , ABI часто требует, чтобы результаты структуры молча преобразовывались в невидимый указатель..

Можно даже определить другое соглашение о вызовах, которое возвращает больше данных в большем количестве регистров.Но такие новые соглашения нарушат весь объектный код и потребуют перекомпиляции всего (включая даже, в Linux, системные библиотеки, такие как libc.so.6), и, конечно, требуют изменения компилятора.

Конечно, соглашения ABI являются процессорами, связанных с системой и компилятором.

Я не знаю Windows и не знаю, что Windows определяет как ее ABI.

4 голосов
/ 04 января 2012

В дополнение к идее о возможном падении производительности или о том, что возвращение структур по значению, возможно, не поддерживалось в обычные дни, еще одна причина, по которой функции C не используют возврат по значению для структур, заключается в том, что если вы возвращаете структуру, которую вы не можете легко вернуть индикатор успеха / неудачи. Я знаю, что иногда начинаю разрабатывать функцию, возвращающую структуру, которая была инициализирована в функции, но потом я столкнулся с проблемой того, как указать, была ли функция выполнена успешно. У вас есть следующие варианты:

  • гарантия успеха (иногда это возможно)
  • передать указатель на местоположение для кода ошибки
  • имеет поле или значение часового в структуре, которое указывает на успех / неудачу

Только опция 1 не позволяет интерфейсу быть kludge. Второй вариант отрицает цель возврата структуры по значению и фактически затрудняет использование функции для обработки сбоев. Третий вариант просто не очень хороший дизайн почти во всех случаях.

4 голосов
/ 04 января 2012

В pre-ANSI C вы не могли вернуть объект типа структуры, а также не могли передавать аргументы типов структуры.

Из цитаты Криса Торека в comp.lang.c:

Обратите внимание, что V6 C также не поддерживает структурно-значимые аргументы и возвращаемые значения со структурными значениями.

Причина, по которой в настоящее время они не очень используются, заключается в том, что люди предпочитают возвращать указатель на структуру, которая включает в себя только копию указателя, а не копию всего объекта структуры.

0 голосов
/ 04 января 2012

Как правило, функции Windows либо ничего не возвращают, либо коды ошибок, особенно когда дело доходит до возврата структур или классов.

Эффективность может быть проблемой, хотя RVO должен снять накладные расходы.

Я думаю, что основной причиной было сохранение методов в соответствии со стилем кодирования, использованным ранее.

...