Каков хороший общий подход для определения возвращаемых значений в C? - PullRequest
8 голосов
/ 26 января 2010

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

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

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

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

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

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

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

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

Ответы [ 8 ]

4 голосов
/ 26 января 2010

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

4 голосов
/ 26 января 2010

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

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

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

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

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

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

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

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

3 голосов
/ 26 января 2010

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

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

  • если вы используете C99, нет ничего плохого в использовании _Bool; ИМХО, это намного чище, чем просто использовать int

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

    struct foo *foo = create_foo();
    if(!foo) /* handle error */;
    
  • Я стараюсь избегать дела (2); возможно использование EXIT_SUCCESS и EXIT_FAILURE, но, тем не менее, такой подход имеет смысл, только если существует более двух возможных результатов, и вам все равно придется использовать enum 1026 *

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

2 голосов
/ 26 января 2010

Ибо не может не определиться. Ответы да / нет с использованием более конкретного (bool) возвращаемого типа могут помочь поддерживать согласованность. Переходя к интерфейсам более высокого уровня, можно подумать о возврате или обновлении специфической для системы структуры обмена сообщениями / детализации результатов.

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

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

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

    rc = 0; rc + = func1 (); rc + = func2 (); rc + = func3 (); если (rc == 0) успех!

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

2 голосов
/ 26 января 2010

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

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

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

0 голосов
/ 26 января 2010

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

Примерно так: int add(int* result, int a, int b), который хранит a + b в * результате и возвращает 1 (или возвращает 0 и устанавливает errno подходящее значение, например, если a + b больше maxint).

0 голосов
/ 26 января 2010

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

#define SUCCESS  1
#define ERROR    0

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

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

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

И придерживайтесь.

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

0 голосов
/ 26 января 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...