Как я могу использовать read() и write() без включения unistd.h?

Я использовал системные вызовы read() и write() в своей программе БЕЗ включения файла заголовка "unistd.h" в программу. Но все же программа работает и дает ожидаемые результаты.

После запуска программы я подумал, что прочитаю справочную страницу для read() и write().

На странице man 2 для read() и write() в разделе SYNOPSIS упоминается, что мне нужно включить заголовочный файл unistd.h для использования read() или write().

SYNOPSIS
   #include <unistd.h>

   ssize_t read(int fd, void *buf, size_t count);


SYNOPSIS
   #include <unistd.h>

   ssize_t write(int fd, const void *buf, size_t count);

Так что я удивлен, как моя программа работала, хотя я не включил unistd.h?

Ниже моя программа. Это программа для копирования содержимого исходного файла в целевой файл с использованием системных вызовов read() и write().

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>

int main()
{
    /* Declaring the buffer. */
    /* Data read by read() will be stored in this buffer. */
    /* Later when write() is used, write() will take the contents of this buffer and write to the file.*/

    char buffer[512];

    /* Decalring strings to store the source file and target file names. */

    char source[128], target[128];

    /* Declaring integer variables in which integer returned by open() will be stored. */
    /* Note that this program will open a source file, and a target file. So, 2 integers will be needed. */

    int inhandle, outhandle;

    /* Declaring integer variable which will specify how much bytes to read or write.*/

    int bytes;

    /* Taking source filename from keyboard.*/

    printf("\nSource File name: ");
    scanf("%s",source);

    /* Open the source file using open().*/

    inhandle = open(source, O_RDONLY);

    /* If there is error while opening source file.*/

    if (inhandle == -1)
    {
            perror("Error opening source file.\n");
            exit(1);
    }

    /* Taking target filename from keyboard.*/

    printf("\nTarget File name: ");
    scanf("%s",target);

    /* Open the target file using open().*/

    outhandle = open(target, O_CREAT | O_WRONLY, 0660);

    /* If there is error while opening target file.*/

    if (outhandle == -1)
    {
            perror("Error opening target file.\n");
            close(inhandle);
            exit(2);
    }

     /* Below code does following:
       1. First reads (at most) 512 bytes from source file
       2. Then copies them to buffer
       3. If bytes read is greater than 0, write the content stored in buffer to target file.
    */

    while((bytes = read(inhandle, buffer, 512)) > 0)
    {
                    write(outhandle, buffer, bytes);
    }

    /* Close both source and target files. */
    close(inhandle);
    close(outhandle);

    return 0;
}

person sps    schedule 16.02.2015    source источник
comment
Были ли включены все предупреждения и выбран ли современный стандарт? -Wall -Wextra -pedantic -std=c11   -  person Deduplicator    schedule 16.02.2015
comment
@Deduplicator, как использовать -Wall и т. д.? Являются ли они компилятором, который я могу использовать с gcc? например: gcc -Wall hello.c ?   -  person sps    schedule 16.02.2015
comment
Да. Попробуйте их и не игнорируйте предупреждения.   -  person Deduplicator    schedule 16.02.2015
comment
@Deduplicator О, я вижу !! Я использовал -Wall, и gcc выдает предупреждения для read(), write() и (бонус!) close() для неявного объявления. Я попытаюсь прочитать, что именно делают те переключатели gcc, о которых вы упомянули. Спасибо большое.   -  person sps    schedule 16.02.2015


Ответы (1)


Ваша программа работала из-за неявного объявления функции, read() и write() оба возвращают ssize_t, а компилятор предполагает int при неявном объявлении функций, так что это может работать, как вы знаете.

Если вы скомпилируете свою программу с включенными предупреждениями, то компилятор предупредит вас об этом, используя gcc

gcc -Wall -Wextra -Werror

остановит компиляцию, если обнаружит неявно объявленные функции, то есть функции без прототипа.

person Iharob Al Asimi    schedule 16.02.2015
comment
Да что ты правильно сказал. Я использовал переключатель -Wall, и gcc предупредил о неявном объявлении read(), write(), А ТАКЖЕ close(). Я видел на справочной странице, что даже close() нуждается в unistd.h. Затем мне было интересно, что означает IMPLICIT DECLARATION, и теперь я увидел, что вы уже сказали выше, что это значит. Спасибо большое - person sps; 16.02.2015
comment
Вам не обязательно включать включения, вы также можете сделать это ssize_t read(int fd, void *buf, size_t count); в любом месте глобальной области перед вызовом read(), и он сделает то, что должен делать, фактически это то, что будет в заголовке. - person Iharob Al Asimi; 17.02.2015
comment
Попался. Я могу объявить прототип сам, вы имеете в виду правильно. У меня есть вопрос в этом, хотя. Хотя прототип говорит, что второй аргумент должен быть типа void *, как это работает с char *? Второй аргумент, который я передал, — это char *, а не void *. Аналогично, хотя чтение возвращает тип данных size_t, я беру его в int, и он все еще работает... - person sps; 17.02.2015
comment
Во-первых: в c void * можно преобразовать в любой тип указателя без необходимости приведения, поэтому, например, приведение malloc() не требуется. Во-вторых: он не возвращает size_t, это ssize_t, это signed, потому что при ошибке возвращается -1, и если значение недостаточно велико, чтобы переполнить int, тогда вы не увидите проблемы, а иногда ssize_t находится в факт int например на 32битных машинах с glibc так и есть, так что в таком случае это даже не опасно, но лучше использовать ssize_t конечно. - person Iharob Al Asimi; 17.02.2015
comment
Хорошо, я вижу, что void * гибкий. Немного запутался с частью size_t, которую вы сказали, но я могу немного рассказать. Думаю, мне нужно написать несколько программ, чтобы я мог видеть ошибки/предупреждения/выходные данные и пытаться понять. Я надеюсь скоро. Спасибо. - person sps; 17.02.2015
comment
@iharob: Да, вы можете объявить функции самостоятельно, но для этого нет веской причины. Просто #include укажите правильные заголовки и позвольте реализации объявить их за вас. В противном случае опечатка в вашем собственном объявлении может привести к некорректной работе программы. - person Keith Thompson; 17.02.2015
comment
@KeithThompson Я не говорю, что есть причина, я просто пытался заставить ОП понять, что прототипы функций нужны там, где они нужны, и что они там, где в этих заголовках, и в заголовках нужны, константы, используемые в некоторых функциях, объявлены в заголовки и некоторые макросы, поэтому не включать заголовки — не очень хорошая идея. - person Iharob Al Asimi; 17.02.2015
comment
В вашем предыдущем комментарии не упоминалось, что объявление функций вручную - плохая идея. Также стоит упомянуть, что C99 отбросил неявное правило int. - person Keith Thompson; 17.02.2015