Из указанного выше подхода следует сделать одно исключение. Поскольку в некоторых средах компиляции тип данных off_t имеет размерность long long, мы приводим off_t-значения к этому типу и в соответствии с описанием из раздела 5.10 используем спецификатор %lld.
В стандарте C99 для printf() определен модификатор длины z, показывающий, что Результат следующего целочисленного преобразования соответствует типу size_t или ssize_t. Следовательно, вместо использования %ld и приведения к этим типам можно указать %zd для ssize_t и аналогично %zu для size_t. Хотя этот спецификатор доступен в glibc, нам нужно избегать его применения, поскольку он доступен не во всех реализациях UNIX.
В стандарте C99 также определен модификатор длины j, который указывает на то, что соответствующий аргумент имеет тип intmax_t (или uintmax_t) — целочисленный тип, гарантированно достаточно большой для представления целого значения любого типа. По сути, использование приведения к типу (intmax_t) и добавление спецификатора %jd должно заменить приведение к типу (long) и задание спецификатора %ld, а также стать лучшим способом вывода числовых значений типов системных данных. Первый подход справляется и со значениями long long, и с любыми расширенными целочисленными типами, такими как int128_t. Но и в данном случае нам следует избегать применения этой методики, поскольку она доступна не во всех реализациях UNIX.
3.6.3. Прочие вопросы, связанные с портированием
В этом разделе рассматриваются некоторые другие вопросы портирования, с которыми можно столкнуться при написании системных программ.
Инициализация и использование структур
В каждой реализации UNIX указывается диапазон стандартных структур, используемых в различных системных вызовах и библиотечных функциях. Рассмотрим в качестве примера структуру sembuf, которая применяется для представления операции с семафором, выполняемой системным вызовом semop():
struct sembuf {
unsigned short sem_num; /* Номер семафора */
short sem_op; /* Выполняемая операция */
short sem_flg; /* Флаги операции */
};
Хотя в SUSv3 определены такие структуры, как sembuf, важно уяснить следующее.
• Обычно порядок определения полей внутри таких структур не определен.
• В некоторых случаях в такие структуры могут включаться дополнительные поля, имеющие отношение к конкретной реализации.
Таким образом, при использовании следующего инициализатора структуры не удастся обеспечить портируемость:
struct sembuf s = { 3, -1, SEM_UNDO };
Хотя этот инициализатор будет работать в Linux, он не станет работать в других реализациях, где поля в структуре sembuf определены в ином порядке. Чтобы инициализировать такие структуры портируемым образом, следует воспользоваться явно указанными инструкциями присваивания:
struct sembuf s;
s. sem_num = 3;
s. sem_op = -1;
s. sem_flg = SEM_UNDO;
Если применяется C99, то для написания эквивалентной инициализации можно воспользоваться новым синтаксисом:
struct sembuf s = {.sem_num = 3, sem_op = -1, sem_flg = SEM_UNDO };
Порядок следования элементов стандартных структур также придется учитывать, если нужно записать содержимое стандартной структуры в файл. Чтобы обеспечить в данном случае портируемость, мы не можем просто выполнить двоичную запись в структуру. Вместо этого поля структуры должны быть записаны по отдельности (возможно, в текстовом формате) в указанном порядке.
Использование макросов, которых может не быть во всех реализациях
В некоторых случаях макрос может быть не определен во всех реализациях UNIX. Например, широкое распространение получил макрос WCOREDUMP() (проверяет, создается ли дочерним процессом файл дампа ядра), но его определение в SUSv3 отсутствует. Следовательно, этот макрос может быть не представлен в некоторых реализациях UNIX. Чтобы для обеспечения портируемости преодолеть подобные обстоятельства, можно воспользоваться директивой препроцессора языка C #ifdef:
#ifdef WCOREDUMP
/* Использовать макрос WCOREDUMP() */
#endif
Отличия в требуемых заголовочных файлах в разных реализациях
В зависимости от реализации UNIX будут различаться списки необходимых прототипу заголовочных файлов с различными системными вызовами и библиотечными функциями. В данной книге показываются требования применительно к Linux и обращается внимание на любые отклонения от SUSv3.
В некоторых функциях, кратко рассматриваемых в книге, показан конкретный заголовочный файл, сопровождаемый комментарием /* For portability */ (/* Из соображений портируемости */). Это свидетельствует о том, что данный заголовочный файл для Linux или согласно SUSv3 не требуется, но, поскольку некоторым другим (особенно старым) реализациям он может понадобиться, нам приходится включать его в портируемые программы.