Стандарт C теперь ISO / IEC 9989: 2011
Стандарт C 2011 года был опубликован ISO в понедельник 2011-12-19 (или, точнее, опубликовано уведомление о его публикации).веб-сайт комитета 19-го числа; стандарт, возможно, был опубликован так же давно, как 2011-12-08).Смотрите объявление на веб-сайте WG14 .К сожалению, PDF от ISO стоит 338 CHF, и от ANSI 387 USD .
- Вы можете получить PDF для INCITS/ ISO / IEC 9899: 2012 (C2011) от ANSI за 30 долларов США.
- Вы можете получить PDF для INCITS / ISO / IEC 14882: 2012 (C ++ 2011) от ANSI за 30 долларов США.
Основной ответ
Вопрос "Допускаются ли повторные определения типа в C"?Ответ: «Нет - не в стандартах ISO / IEC 9899: 1999 или 9899: 1990».Причина, вероятно, историческая;оригинальные компиляторы C не позволяли этого, поэтому первоначальные стандартизаторы (которым было поручено стандартизировать то, что уже было доступно в компиляторах C) стандартизировали это поведение.
См. ответ по Также , где стандарт C99 запрещает повторные определения типа.Стандарт C11 изменил правило в §6.7 ¶3 на:
3 Если идентификатор не имеет связи, должно быть не более одного объявления идентификатора (в описателе или спецификаторе типа)с той же областью действия и в том же пространстве имен, за исключением того, что:
- имя typedef может быть переопределено для обозначения того же типа, что и в настоящее время, при условии, что этот тип не является изменяемым типом; *Теги 1037 *
- могут быть повторно объявлены, как указано в 6.7.2.3.
Так что теперь есть явный мандат на повторное определение типа в C11.Получите доступность C11-совместимых C-компиляторов.
Для тех, кто все еще использует C99 или более раннюю версию, последующий вопрос, вероятно, таков: «Как мне избежать проблем с повторяющимися typedefs?"
Если вы следуете правилу, что существует один заголовок, который определяет каждый тип, который требуется в более чем одном исходном файле (но может быть много заголовков, определяющих такие типы; каждый отдельный тип находится только в одномзаголовок, хотя), и если этот заголовок используется всякий раз, когда необходим этот тип, вы не столкнетесь с конфликтом.
Вы также можете использовать неполные объявления структуры, если вам нужны только указатели на типы ине нужно выделять фактическую структуру или обращаться к ее членам (непрозрачные типы).Опять же, установите правила о том, какой заголовок объявляет неполный тип, и используйте этот заголовок везде, где требуется тип.
См. Также Что такое внешние переменные в C ;он говорит о переменных, но с типами можно обращаться несколько аналогично.
Вопрос из комментариев
Мне очень нужны "неполные объявления структуры" из-за отдельного препроцессораосложнения, которые запрещают определенные включения.То есть вы говорите, что я не должен вводить определения типа forward-объявлений, если они снова определяются с помощью полного заголовка?
Больше или меньше.Мне на самом деле не приходилось сталкиваться с этим (хотя есть части систем на работе, которые очень близки к тому, чтобы беспокоиться об этом), так что это немного условно, но я считаю, что это должно сработать.
Как правило, заголовок описывает внешние службы, предоставляемые «библиотекой» (одним или несколькими исходными файлами), достаточно подробно, чтобы пользователи библиотеки могли скомпилировать ее.Особенно в случае, когда есть несколько исходных файлов, может также быть внутренний заголовок, который определяет, например, полные типы.
Все заголовки (а) автономны и (б) идемпотентны. Это означает, что вы можете (а) включать заголовок, и все необходимые другие заголовки включаются автоматически, и (б) вы можете включать заголовок несколько раз, не вызывая гнева компилятора. Последнее обычно достигается с помощью охраны заголовков, хотя некоторые предпочитают #pragma once
- но это не переносимо.
Итак, у вас может быть публичный заголовок, подобный этому:
public.h
#ifndef PUBLIC_H_INCLUDED
#define PUBLIC_H_INCLUDED
#include <stddef.h> // size_t
typedef struct mine mine;
typedef struct that that;
extern size_t polymath(const mine *x, const that *y, int z);
#endif /* PUBLIC_H_INCLUDED */
До сих пор, так что не очень спорный (хотя можно законно подозревать, что интерфейс, предоставляемый этой библиотекой является весьма неполным).
private.h
#ifndef PRIVATE_H_INCLUDED
#define PRIVATE_H_INCLUDED
#include "public.h" // Get forward definitions for mine and that types
struct mine { ... };
struct that { ... };
extern mine *m_constructor(int i);
...
#endif /* PRIVATE_H_INCLUDED */
Опять же, не очень спорно. Заголовок public.h
должен быть указан первым; это обеспечивает автоматическую проверку самодостаточности.
Код потребителя
Любой код, которому нужны службы polymath()
, записывает:
#include "public.h"
Это вся информация, необходимая для использования сервиса.
Код провайдера
Любой код в библиотеке, который определяет службы polymath()
, записывает:
#include "private.h"
После этого все работает как обычно.
Код другого провайдера
Если есть другая библиотека (назовите ее multimath()
), которая использует службы polymath()
, тогда этот код будет включать public.h
, как и любой другой потребитель. Если сервисы polymath()
являются частью внешнего интерфейса для multimath()
, тогда открытый заголовок multimath.h
будет включать public.h
(извините, я переключил терминологию ближе к концу, здесь). Если службы multimath()
полностью скрывают службы polymath()
, тогда заголовок multimath.h
не будет включать public.h
, но частный заголовок multimath()
вполне может это сделать или отдельные исходные файлы, которым требуется polymath()
услуги могут включать его при необходимости.
Пока вы неукоснительно соблюдаете дисциплину включения правильного заголовка везде, вы не столкнетесь с проблемой двойного определения.
Если впоследствии вы обнаружите, что один из ваших заголовков содержит две группы определений, одну из которых можно использовать без конфликта, а другую - иногда (или всегда) конфликтовать с каким-то новым заголовком (и службами, объявленными в нем), вам нужно разделить исходный заголовок на два подзаголовка. Каждый подзаголовок индивидуально следует правилам, разработанным здесь. Оригинальный заголовок становится тривиальным - защита заголовка и строки для включения двух отдельных файлов. Весь существующий рабочий код остается нетронутым - хотя зависимости меняются (дополнительные файлы зависят от). Новый код теперь может включать соответствующий приемлемый подзаголовок, а также использовать новый заголовок, который конфликтует с исходным заголовком.
Конечно, у вас может быть два непримиримых заголовка. Для надуманного примера, если есть (плохо спроектированный) заголовок, который объявляет другую версию структуры FILE
(от версии в <stdio.h>
), вы попадаете в ловушку; код может включать в себя либо плохо спроектированный заголовок, либо <stdio.h>
, но не оба одновременно. В этом случае, плохо спроектированный заголовок должен быть изменен, чтобы использовать новое имя (возможно, File
, но, возможно, что-то другое). Вы можете более реалистично столкнуться с этой проблемой, если вам придется объединить код из двух продуктов в один после корпоративного поглощения с некоторыми общими структурами данных, такими как DB_Connection
для соединения с базой данных. В отсутствие функции C ++ namespace
вы застряли с упражнением по переименованию для одной или обеих партий кода.