Подтвердить что ты не робот

Почему конкретные типы данных C объявляются в более чем одном стандартном файле заголовка?

Например, C11 указывает, что size_t должен быть объявлен в следующих заголовочных файлах:

  • stddef.h
  • stdio.h
  • stdlib.h
  • string.h
  • time.h
  • uchar.h
  • wchar.h

При чтении C11 я обнаружил, что существует много других типов данных, объявленных в нескольких стандартных файлах заголовков.

Вопросы

  • Скажем, в случае size_t. Почему не просто в stddef.h для простоты?
  • Скажем, компилятор C реализует size_t в этих файлах заголовков. Гарантируются ли они одинаковое определение в этих файлах заголовков?
4b9b3361

Ответ 1

Скажем, в случае size_t. Почему не просто в stddef.h для простоты?

Тип используется при объявлении функций во всех этих файлах. Если он не был объявлен в <stdio.h>, вы получили бы ошибку компиляции, если только вы не включите <stddef.h>.

Скажем, компилятор C реализует size_t в этих файлах заголовков. Гарантируются ли они одинаковое определение в этих файлах заголовков?

Да, они будут иметь одинаковое определение. Как правило, значение определяется в одном месте в отдельном файле include, который включен другими.

В некоторых случаях может быть возможно изменить определение с помощью параметров компилятора или определить, например, компилятор, который допускает компиляцию 32/64 бит, может определить size_t как 32 или 64-разрядный неподписанный объект в зависимости от цели, определенной на командной строки компилятора.

Ответ 2

В качестве примера функции, объявленной в stdio.h, которая требует, чтобы size_t был предварительно определен, рассмотрим snprintf(). Как бы то ни было, если вы хотите использовать его в своем коде, все, что вам нужно сделать, это #include <stdio.h>. Если size_t был объявлен только в stddef.h, вы должны были бы

#include <stddef.h>
#include <stdio.h>

Не только это, но поскольку stdio.h объявляет snprintf, используете ли вы его или нет, вам нужно будет включать оба файла каждый раз, когда вам нужно что-либо в stdio.h, чтобы избежать ошибок компилятора; stdio.h будет иметь искусственную зависимость от stddef.h. Это приводит к тому, что ваш исходный код становится более длинным и более хрупким (обратите внимание, что если вы измените порядок двух директив, он также сломается). Вместо этого мы пишем файлы заголовков, чтобы они стояли одни и не зависели от других заголовков, и это то, что комитет стандартизации C принял решение для стандартной библиотеки.

Ответ 3

Там тонкая разница между и внутри - реализация полностью бесплатна для определения size_t в одном заголовке, если она определена при включении указанных заголовков. Итак, у вас есть два варианта:

  • Определите size_t в каждом отдельном файле и оберните каждый из них в include guard
  • Определите его в одном файле и оберните его в include guard

И да, size_t должен быть определен как указанный, который (glibc):

typedef unsigned long size_t;

или

typedef unsigned int size_t

Они не говорят, что вы должны быть здравомыслящими, они просто говорят, что это нужно определить в то время, когда кто-то включает один из этих заголовков, потому что они зависят от его определения и могут использоваться независимо. Проще говоря, если вы определяете что-то, зависящее от size_t, тогда size_t необходимо сначала (ранее) определить.

Как (или, вернее, где) вы это делаете, зависит от вашей реализации.

Ответ 4

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

Когда специфицирует список "файлы", где size_t должен быть объявлен, то, что он на самом деле говорит, что директива #include, которая называет любой из этих файлов, должна создать этот идентификатор в глобальной области. Это можно сделать, если файлы со всеми перечисленными именами содержат определение size_t или путем включения size_t в компилятор, но только включение встроенного определения компилятора видит директиву #include с одним указанных имен.