Включает ли ncurses.h в C программирование также stdio.h? - PullRequest
1 голос
/ 19 февраля 2020

Я изучал ncurses.h, потому что я пытался изучить немного аспектов проектирования для c, и наткнулся на утверждение, что stdio.h находится внутри ncurses

  #include <ncurses.h>        /*ncurses.h includes stdio.h*/

Это правда? Если да, то может ли кто-нибудь объяснить мне, почему, и если это означает, что мне больше не нужно включать его снова?

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

Ответы [ 4 ]

5 голосов
/ 19 февраля 2020

Да, включая <ncurses.h> почти наверняка будет включать <stdio.h>. Но я советую не пользоваться этим преимуществом.

Документация для ncurses действительно гласит:

ПРИМЕЧАНИЯ
Файл заголовка <curses.h> автоматически включает заголовочные файлы <stdio.h> и <unctrl.h>.

(ncurses.h является (обычно?) Символом c ссылка на curses.h.)

Томас Дики, главный сопровождающий ncurses, , сообщает нам , что это относится к 1994 году, поэтому вы крайне маловероятно встретите реализация ncurses, которая этого не делает.

Стандарт X / Open Curses, который поддерживает ncurses, говорит (выделение добавлено):

Включение <curses.h> может сделать видимыми все символы из заголовков <stdio.h>, <term.h>, <termios.h> и <wchar.h>.

Кроме того, некоторые функции определены в <ncurses.h> и / или <curses.h> принимают аргументы типа FILE, который определен в <stdio.h>. (Возможно, что-то могло бы использовать тип FILE без включения <stdio.h>, но это было бы придумано и глупо, и о такой возможности не стоит беспокоиться.)

Так что #include <ncurses.h> равно практически гарантированно включает <stdio.h>.

Сказав все это, я советую не пользоваться этой гарантией. Если ваш исходный файл C зависит от вещей, объявленных в <stdio.h>, для стиля вы должны иметь явный #include <stdio.h>, даже если он строго избыточен. В более общем случае исходный файл C должен иметь директиву #include для любого заголовка, от которого он зависит. Это вопрос стиля и ремонтопригодности, а не абсолютное требование. См., Например, этот вопрос . Стандарт C гарантирует, что включение <stdio.h> более одного раза не вызовет проблем.

0 голосов
/ 20 февраля 2020

ncurses 'curses.h включает <stdio.h>, потому что некоторые из стандартных функций (X / Open Curses), которые curses.h объявляют, используют FILE. Стандарт C обозначает FILE как «тип объекта», который некоторые могут буквально прочитать как требующий typedef. Однако были реализации, где FILE является символом, определенным с помощью #define, например,

#define   FILE    struct _iobuf

(цитирование более чем одного BSD: 4.3BSD, SunOS 4, Ultrix). Не углубляясь в его историю, я бы предположил, что изменение, сделавшее его typedef, пришло из кода AT & T, поскольку он обнаруживается в реализациях SVr4.

По этой причине (а также является самым простым способ гарантировать, что FILE объявлен правильно), curses.h будет включать stdio.h.

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

#include <sys/types.h>
#include <sys/stat.h>

, но в соответствующей (системной спецификации c) документации оба заголовка показаны на странице руководства. резюме. Документация POSIX для stat не указывает на это, но использует ее в примерах. Некоторые более новые реализации включают (или предоставляют окольным путем) определения из <sys/types.h>, необходимые для <sys/stat.h>, но для написания portable кода, на который нельзя было полагаться.

curses никогда не был документирован таким образом, например, вы вряд ли найдете страницы руководства, показывающие

#include <stdio.h>
#include <curses.h>

, потому что оригинальные проклятия 4BSD включали <stdio.h>. Интересно, что заголовок проклятий 4.2BSD не использовал никакого определения из stdio.h. Он также включал sgtty.h (аналог termios.h), но использовал эти определения в заголовочном файле. Возможно, оригинальный разработчик подумал, что включение stdio.h было хорошей идеей. В любом случае, в более поздних версиях проклятий он действительно использовал FILE, и, поскольку прецедент включения stdio.h. из curses.h был хорошо установлен, никто не рассматривал деление вещей, как это было сделано с <sys/types.h> и <sys/stat.h>.

В документации ncurses упоминаются файлы заголовков, которые curses.h включает в раздел NOTES :

Заголовочный файл <curses.h> автоматически включает заголовочные файлы <stdio.h> и <unctrl.h>.

То примечание восходит к ncurses 1.8.7 в 1994 , поэтому маловероятно, что вы встретите версию ncurses, для которой утверждение неверно.

X / Open Curses имеет нечто похожее на скажем:

Включение <curses.h> может сделать видимыми все символы из заголовков <stdio.h>, <term.h>, <termios.h> и <wchar.h>.

Например, проклятия HPUX включают <term.h> из <curses.h> для объявления setupterm в curses.h, но ncurses (и проклятие Solaris s) нет. AIX curses включает в себя <term.h> и <termios.h>. Опять же, ncurses (и проклятия Solaris) нет. X / Open Curses говорит, что «может сделать видимым» , потому что включение заголовочного файла не обязательно делает всех символов в нем видимыми (есть ifdef для рассмотрения).

Тем не менее, эти функции в любых X / Open Curses (то есть, что-либо более новое, чем 1990) используют FILE, что делает <stdio.h> необходимым для <curses.h>:

Сейчас ... включая <stdio.h> до <curses.h>, вероятно, не будет иметь никакого значения (кроме добавления беспорядка в вашу программу). Возможно (но маловероятно), что может быть какой-то символ, переопределенный в <curses.h>, который изменяет поведение стандартных функций в <stdio.h>. Поскольку любая реализация stdio.h, с которой вы, вероятно, столкнетесь, имеет include-охранники (функция 1990-х годов - не встречалась в заголовках SunOS 4 1994 года), включая <stdio.h> после <curses.h>, также маловероятно изменить поведение вашей программы (опять же, беспорядок, являющийся единственным результатом).

Включающие охранники были предпочтительным стилем для системных заголовков с 1990-х годов. У меня есть скрипт (называемый Include), который я использую для заголовков компиляции тестов, которые я написал в 1998 году, чтобы проверить, что данный заголовок включает в себя необходимые заголовки (такие как в этом случае), чтобы обеспечить все необходимых typedefs и символов, используемых в заголовочном файле.

0 голосов
/ 19 февраля 2020

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

  1. Не вредно включать один и тот же файл несколько раз, если он содержит защитные элементы (т. Е. #pragma once или #ifndef _HEADER_NAME ... #endif)
  2. Технически вы полагаетесь на детали реализации, если вы пропускаете включение, которое стандарт C (или документация API) говорит, что это правильный файл для включения в определенную декларацию или определение. Это означает, что следующая версия вашего компилятора или библиотеки вполне может удалить это удобное вложенное включение, которое делает вашу программу на несколько строк короче.
0 голосов
/ 19 февраля 2020

Это действительно включает stdio.h, и да, это означает, что вам не нужно снова включать stdio.h. Это работает, потому что компилятор вставит содержимое заголовочных файлов #include 'd в ту точку кода, в которой была размещена директива #include. Это означает, что не только ncurses.h было включено в ваш код в указанном вами месте, но stdio.h уже было включено в этот файл в соответствующем месте до того, как он был добавлен в ваш код.

Последующие включения stdio.h не повлияет на вашу программу из-за использования include guard .

Если вы используете что-либо из stdio.h в своем собственном коде, я бы счел это хорошим попрактикуйтесь в добавлении директивы #include <stdio.h> к своим исходным файлам. Я полагаю, что он напрямую сообщает следующему разработчику, читающему ваш код, о том, что здесь используется функциональность stdio, не полагаясь на неявную информацию.

...