Что означает часть SYNOPSIS на справочной странице perror?

Раздел СИНТАКСИС на справочной странице perror:

   #include <stdio.h>

   void perror(const char *s);

   #include <errno.h>

   const char * const sys_errlist[];
   int sys_nerr;
   int errno;       /* Not really declared this way; see errno(3) */

согласно спецификации справочной страницы, в разделе СИНТАКСИС указано, что

Для функций он показывает все необходимые объявления данных или директивы #include, за которыми следует объявление функции.

Следующий код:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *ls_args[2] = {"nonsense", NULL};
    execv(ls_args[0], ls_args); // no return
    perror("execve failed");
    return 2;
}

выводит сообщение об ошибке execve failed: No such file or directory, при этом соответствующее errno равно 2

поскольку errno является глобальной переменной (фактически макросом), определенной в errno.h, а заголовок errno.h не включен, как этот код запускает модификацию errno?

Что означает #include <errno.h> ... int errno; в разделе ОБЗОР? похоже, что perror() можно вызвать и без этой части кода, спасибо!


person mzoz    schedule 28.02.2020    source источник
comment
Синопсис говорит вам, что включение stdio.h даст вам объявление perror, а включение errno.h даст вам объявления sys_nerr и errno. Образец кода не ссылается ни на один из двух файлов из errno.h.   -  person William Pursell    schedule 28.02.2020
comment
Привет @WilliamPursell Интересно, почему часть #include<errno.h> ... включена в синопсис perror(), если для вызова perror() это вообще не требуется, спасибо!   -  person mzoz    schedule 28.02.2020
comment
Это говорит вам, что вам это нужно, если вы хотите напрямую ссылаться на sys_nerr или errno   -  person William Pursell    schedule 28.02.2020
comment
не могли бы вы объяснить механизм errno изменения за кулисами, учитывая, что он не включен в первую очередь   -  person mzoz    schedule 28.02.2020
comment
Заголовки предоставляют объявления для компилятора, но они не имеют ничего общего с компоновкой.   -  person William Pursell    schedule 28.02.2020
comment
errno определяется стандартом ISO C как изменяемое lvalue типа int и не должно быть явно объявлено; errno может быть макросом. errno это thread-local; установка его в одном потоке не влияет на его значение в любом другом потоке. (от man 3 errno)   -  person David C. Rankin    schedule 28.02.2020
comment
как этот код запускает модификацию errno? Значение errno может быть записано/прочитано любой функцией, выполняемой потоком. Это означает, что execv может и меняет его, когда что-то идет не так. Кстати, комментарий no return верен только тогда, когда execv завершается успешно. Когда execv дает сбой, он действительно возвращается после записи кода ошибки в errno.   -  person user3386109    schedule 28.02.2020
comment
@user3386109 user3386109, но execv объявлен в unistd.h, который не пересекается с errno.h, как он может получить доступ и изменить errno? Я также читал, что errno можно модифицировать библиотечными функциями, просто любопытно, как это реализовано.   -  person mzoz    schedule 28.02.2020
comment
Код библиотеки, реализующий execv() или perror(), содержит #include <errno.h>, поэтому он может использовать errno. Точно так же, как в вашем коде.   -  person Jonathan Leffler    schedule 28.02.2020
comment
Привет @JonathanLeffler Я тоже думал об этом, но не могу найти файлы c в glibc github.com/bminor/glibc, не могли бы вы посмотреть и найти их? Большое спасибо!   -  person mzoz    schedule 28.02.2020
comment
@JonathanLeffler в папке include есть только файлы заголовков   -  person mzoz    schedule 28.02.2020
comment
См.: perror() и execv().   -  person Jonathan Leffler    schedule 28.02.2020
comment
@JonathanLeffler мило, спасибо!   -  person mzoz    schedule 28.02.2020
comment
И да, каталог include должен содержать только заголовки (и подкаталоги, содержащие больше заголовков и подкаталогов). Так и должно быть. Ваш компилятор связывает ваш код с предварительно скомпилированной библиотекой (например, glibc), которая содержит объектный код для всех функций, которые вы можете вызывать из системной библиотеки.   -  person Jonathan Leffler    schedule 28.02.2020


Ответы (1)


В соответствии со стандартом C макрос errno объявлен в errno.h, и вы должны явно указать errno.h, если хотите написать переносимую программу, использующую errno. Синопсис man-страницы говорит вам об этом. (Это не говорит о том, что вам нужно включить errno.h, чтобы использовать perror. Иногда в разделе «Синопсис» рассказывается о других связанных библиотечных возможностях.)

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

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

person rici    schedule 28.02.2020