Я создаю программное обеспечение с помощью Arm-none-eabi-gcc 10.2 (-01, -Wall)
У меня есть следующие файлы (очевидно, это не те файлы, которые мне не разрешено публиковать):
заголовок1.h
// variable definition
bool b_myBool;
Тип bool переобъявлен в моем программном обеспечении на unsigned char
header2.h
// variable declaration, const is to be sure the variable is not modified in this translation unit
extern const bool b_myBool;
файл2.c
#include <header2.h>
void myFunction(void)
{
bool l_localBool;
...
if (l_localBool != b_myBool)
{
do something;
}
}
Теперь я случайно изменил header2.h так:
extern const float b_myBool;
И я не получил никакого предупреждения (несмотря на опцию компиляции -Wall), ни потому, что тип объявления и определения не совпадают, ни потому, что операнды сравнения имеют разные типы (я знаю, что сравнение чисел с плавающей запятой и целых чисел определяется стандартом, поскольку int будет преобразован в float, просто странно, что об этом нет предупреждения).
Действительно, конфликта времени компиляции нет. Поэтому отсутствие предупреждения о несоответствии определения/объявления имеет смысл.
Почему у вас есть определение в заголовочном файле?!
@n.m.couldbeanAI это редкость, но не запрещено. Цель состоит в том, чтобы контролировать, какой файл .c использует какую переменную. Используется в критически важных системах безопасности.





Наиболее распространенная модель, используемая для реализации C, заключается в том, что каждая единица перевода1 компилируется отдельно, и в объектные модули, создаваемые в результате компиляции, включается мало информации о типах символов или она вообще отсутствует. Когда компоновщик связывает вместе разные объектные модули, он не может выполнять проверку типов, поскольку информация о типе отсутствует в объектных модулях.
Вы показываете объявление b_myBool в header1.h2, но не показываете header1.h, включенное в какой-либо другой файл, особенно в file2.c. Поскольку header1.h не включен в file2.c, а header2.h есть, компилятор не видит объявление в header1.h во время компиляции file2.c и, следовательно, не может сверить объявление в header1.h с объявлением в header2.h.
Если вам необходим внешне связанный объект, большинство программистов на C используют следующую практику:
foo.c.foo.h.#include, чтобы включить foo.h в foo.c и в каждый исходный файл, использующий этот объект.Тот факт, что foo.h включен в foo.c, означает, что компилятор увидит и определение в исходном файле, и объявление в заголовке во время одной и той же компиляции, поэтому он может и будет проверять тип. Тогда тот факт, что foo.h включен в каждый другой исходный файл, которому нужен объект, означает, что он получит то же объявление, которое проверяется при компиляции foo.c.
1 Единица перевода — это весь компилируемый исходный код, полученный из одного файла исходного кода, в котором все его #include операторы заменены включенными заголовочными файлами.
2 Каждое определение — это декларация. bool b_myBool; — это предварительное определение. Предварительное определение на самом деле не является определением, но может привести к созданию определения.
И я не получил никакого предупреждения (несмотря на опцию компиляции -Wall), ни потому, что тип объявления и определения не совпадают, ни потому, что операнды сравнения имеют разные типы (я знаю, что сравнение float и int определяется стандартом, поскольку int будет преобразовано в число с плавающей запятой, просто странно, что об этом нет предупреждения).
Нет, это не так, проблема в том, что вы используете в своем коде два разных определения (в двух отдельных заголовочных файлах) и изменили тип переменной только в одной единице компиляции (в единице компиляции, которая определена в неправильный заголовочный файл).
Для решения этой проблемы были введены заголовочные файлы, но компилятор, компилирующий объявление extern, не может узнать, что существует другая единица компиляции, в которой переменная определена иначе.
файлы заголовков делают объявление extern общим для кода клиента и кода сервера (сервера самой переменной), чтобы иметь общее объявление, которое является общим. Ваша проблема в том, что вы используете два разных файла заголовков с разными объявлениями переменной. Компилятор не знает ничего, что было скомпилировано отдельно, поэтому вы не получите даже предупреждения, потому что вы сообщили компилятору, что в этой компиляции переменная определена по-другому (но определена в другом месте, а не здесь).
Компилятору необходимо знать тип переменной в обоих файлах... но вы использовали разные объявления, поскольку они находятся в разных файлах заголовков, которые не включены в пересекающиеся единицы компиляции.
Даже если вы скомпилируете оба модуля компиляции в одной и той же командной строке, эти две компиляции независимы, что делает это разное объявление невидимым при одной компиляции при другой. Если вы хотите, чтобы функция была объявлена в каком-либо модуле компиляции и была доступна только в режиме только для чтения из остальной части юниверса, объявите функцию доступа, которая дает вам копию значения (ну, вы можете прочитать ее и изменить прочитанное значение, но вы не можете изменить источник) и объявите переменную static в модуле реализации, где эта переменная фактически определена.
Знаете ли вы о понятии единиц перевода? Сам компилятор имеет дело только с единицами перевода, которые, короче говоря, представляют собой единый источник со всем включенным в него заголовочным файлом. Если у вас есть один глобальный символ, объявленный по-разному в разных единицах перевода, то поведение будет неопределенным. К сожалению, компилятор или компоновщик ничего не могут обнаружить.