C проверка ошибок в стандартной библиотеке (ret <0 vs ret == -1)

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

ret = function();
if (ret < 0) {
    ...
}

против

ret = function();
if (ret == -1) {
    ...
}

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

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

Ответ 1

В основном это историческая катастрофа. Раньше были (до POSIX) UNIX-подобные системы, которые возвращали -errno при ошибке вместо -1, поэтому вам не пришлось использовать глобальную переменную errno. Таким образом, проверка на < 0 будет работать на таких системах.

Вы можете увидеть эту историю еще в Linux - системные вызовы возвращают -errno, а библиотека C проверяет значения в диапазоне [-4096..- 1]. Если возвращаемое значение находится в этом диапазоне, библиотека сбрасывает его и сохраняет в errno (что ядро ​​ничего не знает), а затем возвращает -1.

Ответ 2

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

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

В-третьих, в целом проверка "отрицательного" значения для многих (если не для большинства или всех) процессоров более эффективна для проверки определенного значения. Многие CPU имеют выделенный флаг состояния CPU, который обозначает отрицательный результат. Это означает, что тестирование отрицательных значений не обязательно должно содержать литерал-операнд. Например, на платформе x86 тест на негативность не должен включать сравнение с литералом 0. Сравнение со специфическими значениями, как правило, требует вложения этого конкретного значения в машинную команду, что приводит к более медленной инструкции. Однако в любом случае "специальные" значения, такие как 0, -1, 1 и т.д., Могут быть проверены на использование более умных и эффективных методов. Поэтому я сомневаюсь, что соответствующие сравнения сделаны по причинам производительности.

Ответ 3

Если функция определена для возврата -1 при ошибке и 0 при успешном завершении, используйте -1 для проверки ошибок и 0 для проверки на успех.

Выполнение чего-либо еще неточно и увеличивает вероятность сбоя.

Если кто-то хочет быть на стороне сохранения, следует попытаться обнаружить все случаи, когда функция возвращает значение, не определяемое как возвращаемое значение, и делает addtional assert в этом случае. Для моего примера внедрения это будут все значения >0 и <-1.

Что это.

int func(void); /* Returns 0 on success and -1 on error. */

int main(void)
{
  int result = func();

  switch result
  {
    case 0:
      /* Success. */
      break;

    case -1:
      /* Failure. */
      break;

    default:
      /* Unexpected result! */
      break;
   }

   return 0;
}