Что такое хороший общий подход для решения возвращаемых значений в C?

Моя программа записана в C для Linux и имеет много функций с различными шаблонами для возвращаемых значений:

1) один или два возвращаются n на успехе и -1 при отказе.
2) некоторый возврат 0 на успехе и -1 при отказе.
3) некоторый возврат 1 на успехе и 0 при отказе (я обычно отвергаю использование булева типа).
4) возврат указателей 0 при отказе (я обычно отвергаю использование NULL).

Мой беспорядок возникает по первым трем - функции, которые возвращаются, указатели всегда возвращаются 0 при отказе это легко.

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

Вторая опция, обычно связан с функциями обработки командной строки, но я не уверен, она имеет правильность, возможно, лучшие значения были бы EXIT_SUCCESS и EXIT_FAILURE?

Третья опция предназначается для функций, которые являются удобными и естественными, чтобы быть названными в условиях, и я обычно эмулирую булев тип здесь, при помощи int значения 1 и 0.

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

Таким образом, как я могу добавить ясность к своему подходу при решении типы возврата здесь?

9
задан James Morris 26 January 2010 в 19:04
поделиться

8 ответов

Итак, как я могу внести ясность в свой подход при принятии решения о типах возврата здесь?

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

  • Если вы делаете много системных вызовов, то любая целочисленная функция возврата должна возвращать -1 при отказе.

  • Если вы не осуществляете системных вызовов, вы можете следовать соглашению структур управления C, согласно которому ненулевое значение означает успех, а нулевое - сбой. (Не знаю, почему вам не нравится bool.)

  • Если функция возвращает указатель, то сбой следует указать на сбой, вернув NULL.

  • Если функция возвращает число с плавающей запятой, то сбой следует указать на сбой, вернув NaN.

  • Если функция возвращает полный диапазон знаковых и неподписанных целых чисел, то, вероятно, в возвращаемом значении не следует кодировать успешность или неудачу.

Тестирование возвращаемых значений - это бред для программистов на языке Си. Если сбой происходит редко и вы можете написать центральный обработчик, рассмотрите возможность использования макропакета исключений , который может указывать на сбой, используя longjmp .

5
ответ дан 4 December 2019 в 13:01
поделиться

не может потерпеть неудачу детерминированным. Да / Нет ответов, используя более конкретный (Bool) возвращаемый тип, может помочь сохранить согласованность. Сдвиньте дальше для интерфейсов более высокого уровня, можно захотеть думать о возврате или обновлении системных систем. Структура деталей обмена сообщениями / результатом.

Мое предпочтение 0, чтобы всегда быть успешным, основано на следующих идей:

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

  2. Когда успех равен нулю, можно сделать кучу ортогональных вызовов и проверять группу успехов в одном условии, просто, сравнивая код возврата группы.

    RC = 0; RC + = Func1 (); RC + = Func2 (); RC + = Func3 (); Если (RC == 0) успех!

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

2
ответ дан 4 December 2019 в 13:01
поделиться

Это вопрос предпочтений, но то, что я заметил, это несоответствие. Рассмотрим это с помощью компилятора Pre C99

#define SUCCESS  1
#define ERROR    0

, а затем любая функция, которая возвращает INT, вернуть либо одну, либо другой, чтобы минимизировать путаницу и придерживаться его религиозным. Опять же, в зависимости от и учитывая команду разработки, придерживайтесь их стандарта.

В предварительных компиляторах C99 int of Zero является ложным, и что-то больше, чем ноль, должно быть правдой. Это зависит от того, на каком стандарте ваш компилятор, если это C99, используйте тип stdbool _bool.

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

и сохранить последовательное.

Надеюсь, это поможет, С уважением, Том.

0
ответ дан 4 December 2019 в 13:01
поделиться

Большая часть стандартной библиотеки C использует стратегию, чтобы вернуть только true (или 1) на успехе и ложь (или 0) по ошибкам, и хранить результат в прошествии в месте. Более конкретные коды ошибок, чем «не удалось», сохраняется в специальной переменной errno.

Что-то вроде этого Int Добавить (INT * результат, int a, int b) , который хранит A + B в * результате и возвращает 1 (или возвращает 0 и устанавливает errno для подходящего значения, если + B бывает больше, чем maxint).

0
ответ дан 4 December 2019 в 13:01
поделиться

Одно условие, о котором я могу подумать, где ваша вышеуказанная методология может выйти из строя - это функция, которая может вернуть любое значение, в том числе -1, скажем, функция добавления двух подписанных номеров.

В этом случае тестирование на -1, безусловно, будет плохой идеей.

В случае, если что-то не удается, я бы лучше установил глобальный флаг состояния ошибок, предоставляемый стандартом C в виде errno и использовать это для обработки ошибки.
Хотя стандартная библиотека C ++ предоставляет исключения, которые составляют много трудолюбие за обработку ошибок.

2
ответ дан 4 December 2019 в 13:01
поделиться

Итак, как я могу добавить ясность для моего подхода при принятии решения по тирам возврата здесь?

Просто тот факт, что вы думаете об этом, проходит долгий путь. Если вы придумываете один или два правила - или даже больше, если они имеют смысл (вам может потребоваться более чем одно правило - как вы упоминаете, вы можете обрабатывать возвращенные указатели по-разному, чем другие вещи), я думаю, вам будет лучше чем многие магазины.

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

Самое главное - иметь руководящие принципы, которые следуют. Даже приятнее состоит в том, чтобы иметь руководящие принципы, которые имеют документированное обоснование (я считаю, что с рациональными людьми более склонны следовать руководящим принципам). Как я уже сказал, просто тот факт, что вы думаете об этих вещах, ставит вас впереди многих других.

0
ответ дан 4 December 2019 в 13:01
поделиться

Почему вы не используете метод, используемый стандартной библиотекой C? О, подожди ...

4
ответ дан 4 December 2019 в 13:01
поделиться

Не фактический ответ на ваш вопрос, но некоторые случайные комментарии, которые вы можете найти интересно:

  • Обычно это очевидно, когда использовать корпус (1), но он становится уродливым, когда вносимые без знаки - возврат (size_t_t_t ) -1 все еще работает, но это не красиво

  • , если вы используете C99, нет ничего плохого в использовании _bool ; IMO, это намного чище, чем просто используя INT

  • , я использую возврат NULL вместо возврата 0 в контекстах указателя (предпочтение Perons), но я редко проверяю Это как я нахожу его более естественным, чтобы просто относиться к указателю как логическое; Обычный случай будет выглядеть так:

     struct foo * foo = create_foo ();
    Если (! Foo) / * Ошибка обрабатывает ошибку * /;
     
  • Я стараюсь избегать корпуса (2); Использование EXIT_SUCCESS и EXIT_FAILURE может быть осуществимым, но IMO этот подход имеет смысл только в том случае, если есть более двух возможных результатов, и вам придется использовать Enum В любом случае

  • для более сложных программ, возможно, имеет смысл реализовать собственную схему обработки ошибок; Существуют некоторые довольно передовые реализации, использующие SETJMP () / Longjmp () вокруг, но я предпочитаю что-то errno - подобных разных переменных для разных типов ошибок

​​
3
ответ дан 4 December 2019 в 13:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: