Бібліотека С не встановлена errnoна 0 з історичних причин 1 . POSIX більше не стверджує, що його бібліотеки не змінять значення у випадку успіху, і нова сторінка Linux дляerrno.h цього відображає:
Файл <errno.h>заголовка визначає цілу змінну errno, яка встановлюється системними викликами та деякими функціями бібліотеки у разі помилки, щоб вказати, що пішло не так. Його значення є важливим лише тоді, коли значення, що повертається, викликає помилку (тобто, -1від більшості системних викликів -1або NULLвід більшості бібліотечних функцій); функція, яка є успішною , дозволяється змінювати errno.
ANSI C Обгрунтування стверджує , що Комітет визнав за доцільне прийняти і стандартизувати існуючу практику використання errno.
Зазвичай повідомлення про помилки, орієнтовані на налаштування, errnoяк правило, розцінюються з толерантністю. Він вимагає `` патологічної зв'язку '' між бібліотечними функціями та використовує статичну комірку пам'яті, що записується, що заважає будувати спільні бібліотеки. Тим не менше, Комітет вважав за краще стандартизувати існуючі, хоча і дефіцитні механізми, а не вигадувати щось більш масштабне.
Майже завжди існує спосіб перевірити помилку поза межами перевірки, якщо вона errnoвстановлена. Перевірка наявності errnoвстановленого не завжди є надійною, оскільки для отримання дзвінків потрібні виклики окремого API, щоб отримати причину помилки. Наприклад, ferror()використовується для перевірки на помилку, якщо ви отримаєте короткий результат від fread()або fwrite().
Цікаво, що ваш приклад використання strtod()- це один із випадків, коли для встановлення помилки потрібно встановити errnoзначення 0 перед викликом . У всіх функціях рядка для числення є ця вимога, тому що дійсне значення повернення повертається навіть за умови помилки.strto*()
errno = 0;
char *endptr;
double x = strtod(str1, &endptr);
if (endptr == str1) {
/*...parse error */
} else if (errno == ERANGE) {
if (x == 0) {
/*...underflow */
} else if (x == HUGE_VAL) {
/*...positive overflow */
} else if (x == -HUGE_VAL) {
/*...negative overflow */
} else {
/*...unknown range error? */
}
}
Вищевказаний код заснований на поведінці, strtod()як це задокументовано в Linux . Стандарт C лише передбачає, що під потоком не може повернутись значення, що перевищує найменший позитивний показник double, і встановлено, чи не errnoвизначено ERANGEреалізацією 2 .
Насправді існує обширний довідковий запис cert, який рекомендує завжди встановлювати errnoзначення 0 перед викликом бібліотеки та перевіряти його значення після виклику вказує на помилку . Це відбувається тому, що деякі дзвінки до бібліотеки встановлюються, errnoнавіть якщо сам виклик був успішним 3 .
Значення errnoдорівнює 0 при запуску програми, але воно ніколи не встановлюється 0 жодними функціями бібліотеки. Значення errnoможе бути встановлене на ненульове значення викликом функції бібліотеки, чи є помилка чи ні, за умови, що використання errnoне зафіксовано в описі функції в Стандарті C. Програма має сенс перевіряти вміст errnoлише після повідомлення про помилку. Точніше, errnoце має сенс лише після того, як функція бібліотеки, яка встановлює errnoпомилку, повернула код помилки.
1. Раніше я стверджував, що слід уникати маскування помилки під час попереднього дзвінка. Я не можу знайти жодних доказів, які б підтвердили цю вимогу. У мене також був хибний printf()приклад.
2. Дякуємо @chux за вказівку на це. Посилання є C.11 §7.22.1.3 ¶10.
3. Вказав @KeithThompson у коментарі.
errno, ви завжди можете встановити його на нуль.