Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Заголовочные файлы и модули.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Smoky
Добрый день. Всегда размещал специфические функции в заголовочных файлах .h. Но вот знающие люди предлагают в заголовочных файлах только объявлять переменные и функции а сами функции размещать в отдельном модуле с расширением .с. Сделал я такую попытку и компилятор сразу же потерял эти функции. В модуле и в основном файле проекта строка #include на заголовочный файл .h была объявлена. Как правильно поступать в таком случае и стоит ли это делать?
sigmaN
Думаю что эти функции потерял не компилятор, а линкер.
Очевидно эти .c файлы вы забыли добавить в проект(или в makefile) или в командную строку компилятора... нужно больше подробностей о том какой компилятор используется и как происходит сборка проекта.

Реализацию функций делать в .c файлах, а описание прототипов в .h - это стандартный, можно сказать канонический способ написания программ на Си.
Более того - если в .h файле сделать реализацию функции, а потом включить(#include) этот .h файл в двух разных .c файлах одного проекта то линкер потом будет возмущен наличием двух функций с одинаковыми именами в двух разных .obj файлах.

Таки да, иногда, для определенных целей делают реализации в .h но это всё, как правило, разные оптимизации, хаки, и прочие тонкости. Каждый такой случай рассматривать надо отдельно и скорее всего его можно будет оспорить.

Единственным правильным случаем включения реализации в .h файл является случай описания шаблона(template) в языке С++. Но это тоже отдельная и ОЧЕНЬ большая тема....
Baser
Пару раз встречал расположение функций в файлах .h, но это точно были исключительные случаи.
Больше типа функции-макросы или weak-шаблоны функций.

Стандартная практика:
Пары файлов xxx.c и xxx.h
где в .с объявляются глобальные и статические переменные и функции модуля
и прототипы статических функций.
.c файлы в явном виде включаются в проект.

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

Чтобы .h файл не включался больше одного раза в другие файлы, применяется условная трансляция.
Код
#ifndef __XXX_H
#define __XXX_H
тело файла
#endif  /* __XXX_H */
Smoky
Цитата(sigmaN @ Dec 31 2017, 02:01) *
Думаю что эти функции потерял не компилятор, а линкер.
Очевидно эти .c файлы вы забыли добавить в проект(или в makefile) или в командную строку компилятора... нужно больше подробностей о том какой компилятор используется и как происходит сборка проекта.


Использую AVRStudio 4.19, вернулся к ней после годового использования Atmel Studio 7.0, нет смысла использовать столь громоздкую систему менее чем на 10%. На AVRStudio имеется, хоть и краткое, руководство без объяснения таких тонкостей как подключение .с файлов. Был бы очень признателен за информацию об их подключении к проектам, ибо кроме директивы #include мне не известно. Спасибо.

Цитата(Baser @ Dec 31 2017, 04:52) *
Чтобы .h файл не включался больше одного раза в другие файлы, применяется условная трансляция.
Код
#ifndef __XXX_H
#define __XXX_H
тело файла
#endif  /* __XXX_H */


Спасибо, теперь стало ясно для чего используют эти директивы препроцессора в .h файлах.
dxp
Помещение определений встраиваемых функций (inline) в заголовочные файлы - стандартная практика. Определения обычных функций помещать в заголовочные файлы нельзя, иначе будет ошибка сборки типа redeclaration error, что логично и понятно.
gosha-z
Цитата(dxp @ Dec 31 2017, 13:21) *
Помещение определений встраиваемых функций (inline) в заголовочные файлы - стандартная практика. Определения обычных функций помещать в заголовочные файлы нельзя, иначе будет ошибка сборки типа redeclaration error, что логично и понятно.

Именно определения (без реализации) и надо помещать, иначе получите implicit declaration
Сергей Борщ
QUOTE (gosha-z @ Dec 31 2017, 14:54) *
Именно определения (без реализации) и надо помещать,
Определение функции содержит ее реализацию. То, что вы имеете ввиду, называется "объявление функции".
gosha-z
Цитата(Сергей Борщ @ Dec 31 2017, 19:33) *
Определение функции содержит ее реализацию. То, что вы имеете ввиду, называется "объявление функции".

Согласен, неправильно выразился.
Smoky
Разобрав несколько примеров сделал попытку переделать один из своих проектов, разделив свои заголовочные файлы на .c и .h. Как только я это сделал с одним файлом компилятор сразу же "потерял" функции в других заголовочных файлах, которые я не изменял. По "пыжившись" без результатов вернул всё в исходное состояние, вероятно нужно начинать эти "эксперименты" в новых учебных проектах. И всё же, может не стоит это делать, особенно самоучкам? Размещая функции в заголовочных файлах директива #include на них объявляется только в основном файле .c проекта. При разделении на модули приходится гадать, где и какие объявлять директивы #include, при разборе примеров я, честно говоря, не смог постичь логику их объявления.
HardEgor
Цитата(Smoky @ Jan 1 2018, 12:55) *
Разобрав несколько примеров сделал попытку переделать один из своих проектов, разделив свои заголовочные файлы на .c и .h. Как только я это сделал с одним файлом компилятор сразу же "потерял" функции в других заголовочных файлах, которые я не изменял.

Лучше приложите файлы, тогда можно подсказать чего не хватает.

В общем случае, надо понимать, что .h-файлы сделаны для так называемой "изоляции кода", т.е. они описывают интерфейс доступа к вашему .c-файлу. Чтобы пользователь .c-файла (или скомпилированной библиотеки функций в виде .dll или .ld) не копался в вашем исходном коде(если он есть), а открыв .h-файл узнал какие функции есть и как ими пользоваться, а компилятор знал как на эти функции сослаться.
Smoky
Цитата(HardEgor @ Jan 1 2018, 13:42) *
Лучше приложите файлы, тогда можно подсказать чего не хватает.


В проекте 17 заголовочных файлов и это только половина... К тому же я всё вернул в исходное, может и не корректное но рабочее состояние.
sigmaN
Так вы логики в .h файлах не видите потому не понимаете процесс компиляции и сборки проекта.

В Си вам можно обойтись и без .h файлов, но не наоборот. Так вот то то вы делаете - это такой извращенный способ сформирвать один большой .с файл.
Препроцессор же вместо #include "header.h" просто вставляет текст из файла header.h. Ни больше ни меньше. Вы щас получаете большой .c файл, который отправляется на копиляцию.

По-пробую как-то сжато передать суть...
Для упрощения пока не рассматриваем данные и скажем, что по сути каждый .c файл это тупо набор функций - кусков исполняемого кода который можно вызвать по имени.
Для того чтобы вызвать этот кусок кода надо знать имя, типы возвращаемого функцией значения и типы аргументов функции(плюс их кол-во).
В общем случае мы имеем несколько .с файлов, которые вызывают функции друг друга.
Но компиляция этих файлов происходит независимо!
Допустим, у вас в проекте три .с файла. 1.с 2.с 3.с Это будет означать, что надо ТРИ раза вызвать компилятор и на выходе мы получим три объектных модуля 1.obj 2.obj 3.obj
Эти объектные модули - полуфабрикаты, в которых указано имя функции и исполняемый код ей соответствующий. Данные мы пока не рассматриваем, как я уже и говорил.

Вы же помните да, что чтобы вызвать функцию надо знать её имя, тип результата и аргументов? Это необходимо чтобы сгенерировать код вызова этой фнкции.
Так вот это и есть тот самый прототип функции.
Мы в файле 1.с хотим использовать фукнцию void func(int a); которая реализована в файле 2.с и магия в том, что нам для этого нужно знать только прототип функции.
Помните же да, что компилятор компилирует один .с файл за один раз. Может быть файл 1.с поступил на компиляцию раньше и машинного кода для 2.с еще вовсе нет! Это не проблема, нам достаточно прототипа функции. Дальше линкер разберется.. Но об этом позднее.

Так вот мы могли бы в файле 1.с ПЕРЕД вызовом func(1); объявить её прототип. Написать void func(int a);
Тогда не возникнет никаких проблем и компилятор будет знать что func(1) соответствует прототипу и сможет с генерировать машинный код для вызова. Как видите, никаких #include и .h файлов вообще не нужно!

Однако это очень неудобно, потому что функция func() может потребоваться нам и в файле 1.с и в файле 10.с и еще в многих местах! Копипаситить прототипы теперь везде? Было бы глупо.
Выход простой: Функция func() реализована в файле 2.с, поэтому очень логично сделать файл 2.h и поместить туда прототипы всех функций реализованных в файле 2.с.

Теперь любой .с файл, который хочет использовать функции реализованные в 2.с просто делает #include 2.h и таким образом в начале этого .с файла препроцессор подставляет все прототипы всех функций из файла 2.с и мы можем свободно их вызывать из любых других .с файлов. Прототипы теперь описаны один раз и хранятся централизовано. Этот файл еще называют интерфейсом модуля. В этом-же .h файле могут хранится описания типов данных, но этого мы пока не касаемся для упрощения понимания.


Также стоит отметить, что нет такого понятия как "основной .с файл проекта", которое вы тут используете.
Есть просто требование, что где-то должна обязательная быть функция main(), которая будет вызвана из стартапа, после того как будет подготовлено Си окружение(инициализирована память, указатель стека и т.д.)



Короче после того как компилятор был вызван 100500 раз, ОТДЕЛЬНО для каждого .с файла и на выходе получены .obj файлы
https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%...%83%D0%BB%D1%8C
Начинается компоновка(линкинг) вызывается линкер, которому скармливаются все эти объектные модули(и не только, но это щас не важно).
Линкер размещает каждый .obj файл по определенному адресу и таким образом знает какая функция из какого .obj расположена по какому адресу в памяти.
Я сейчас намеренно всё очень упрощаю и не касаюсь темы данных, секци и т.д...

Далее линкер проходится по всем вызовам функций и заменяет вызов по имени на конкретный адрес! Ведь он теперь знает адреса всех функций из всех .obj файлов.
Например 1.obj вызывается функция func(). Линкер находит ее реализацию в модуле 2.obj и в то место подставляет уже АДРЕС. Либо выдаст ошибку если нигде ни в одном из .obj которые ему передали он эту функкцию не нашел. Либо если в двух разных .obj есть две функции с одинаковым именем.



Так вот вы разделите свой проект исходя из вышеописанной логики и обязательно добавьте все свои .c файлы в проект.
IDE AVRStudio для вас сделает всю магию - т.е. будет вызывать компилятор отдельно для каждого .c файла, потом все полученные результаты передаст линкеру и на выходе у вас получится один исполняемый файл. Это всё произойдет автоматически.

Во всем этом разделении есть очень много логики и смысла, я лишь ОЧЕНЬ упрощенно показал вам процесс.
Без дальнейшего изучения тут не обойтись. Есть static функции которые из других .c файлов вызвать нельзя. Есть inline функции которые вместо вызова сразу встраиваются в точку вызова... Есть много тонкостей разных которые требуют отдельного освещения и имеют свои тонкости.

С Новым Годом всех, успехов в разработках и изучении Си )))
nice_vladi
Цитата(sigmaN @ Jan 1 2018, 12:45) *
...

По-пробую как-то сжато передать суть...

...


ОФФТОП.
Спасибо за изложение. Что-то встало на свое место в голове. Я просто мимо крокодил)
Smoky
Цитата(sigmaN @ Jan 1 2018, 18:45) *
Так вы логики в .h файлах не видите потому не понимаете процесс компиляции и сборки проекта.

С Новым Годом всех, успехов в разработках и изучении Си )))


Логику использования .h файлов я понял, я не уловил в примерах закономерности директив #include в модулях .c. Всё что вы изложили было очень полезным, спасибо. Ну и конечно же с Новым Годом!
sigmaN
Цитата(Smoky @ Jan 1 2018, 17:54) *
Логику использования .h файлов я понял, я не уловил в примерах закономерности директив #include в модулях .c.

Ну тогда надо начинать с рассмотрения этих конкретных примеров и искать логику. Может быть ее там и действительно нет )
Baser
Цитата(Smoky @ Jan 1 2018, 07:55) *
Разобрав несколько примеров сделал попытку переделать один из своих проектов, разделив свои заголовочные файлы на .c и .h. Как только я это сделал с одним файлом компилятор сразу же "потерял" функции в других заголовочных файлах, которые я не изменял.

Переделка только одного файла может только запутать все эти перекрестные ссылки.
Нужно переделывать все. Обычно это довольно трудоемко.
У меня есть опыт перенятия чужих проектов начинающих программистов, которые плохо понимали структуризацию проектов на Си.
На "распутывание" проекта из ~20 файлов уходила пара дней sad.gif
Там еще будут проблемы грамотного ограничения области видимости переменных и функций,
но это вам можно оставить на потом, после вникания в это.

Цитата(Smoky @ Jan 1 2018, 16:54) *
Логику использования .h файлов я понял, я не уловил в примерах закономерности директив #include в модулях .c

Для начала, все .с файлы должны быть включены в проект в самой IDE AVRStudio
Там должно быть окно с деревом файлов проекта. Файлы обычно включаются командой типа: Add file to project

С инклудами .h файлов все просто. Запускаете компиляцию проекта. Например на файл main.c компилятор ругается, что не может найти функцию func(). Вы поиском находите, что эта функция у вас находится в файле uart.c, а в файле uart.h есть описание её прототипа. Значит, в начало файла main.c нужно добавить #include uart.h
И так по всем ошибкам компилятора.

С Новым Годом! santa2.gif
Smoky
Цитата(Baser @ Jan 2 2018, 00:47) *
Переделка только одного файла может только запутать все эти перекрестные ссылки.
Нужно переделывать все. Обычно это довольно трудоемко.


Согласен. Поэтому решил начать сначала. Прошу посмотреть фрагмент программы и сказать что я делаю не так. Этот фрагмент инициализирует LCD 1х20, загружает пользовательские символы, выводит на LCD "Инициализация" и короткий звуковой сигнал 10 мсек. Файлы .c компилируются нормально но вот сборка не получается. Директивы #include вставлял по мере вывода сообщений об ошибках но результата так и не получил...
sigmaN
Мне пришлось импортировать проект в AS 7
Я правильно понимаю, что вы получаете вот такие ошибки?
Код
Severity    Code    Description    Project    File    Line
Error        ld returned 1 exit status    Cloc_Meteo1_2    collect2.exe    0
Error        recipe for target 'Cloc_Meteo1_2.elf' failed    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\default\Makefile    129
Error        multiple definition of `Smil_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `sign_clcdur'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Pikout.c    30
Error        multiple definition of `Sand_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Pres_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Line_message'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\default\Symbol.o    1
Error        multiple definition of `insg'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\default\Pikout.o    1
Error        multiple definition of `Humi_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Hood_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Home_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Grad_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10
Error        multiple definition of `Bell_char'    Cloc_Meteo1_2    D:\Poligon\Cloc_Meteo1_2\Symbol.c    10


Пройдемся по ошибке
Sand_char

Делаем поиск по проекту, видим, что в файле Symbol.h определено следующее:
Код
const byte Sand_char[] PROGMEM={0,27,0,4,0,14,17,0}; //Загружаемый символ грусти


Ищем какие файлы инклюдят этот Symbol.h

#include "Symbol.h" имеется в Base.c и в Symbol.c

Вы же помните да, что #include тупо добавляет текст из .h файла в .c файл?
Вот мы и имеем ситуацию, когда в двух единицах трансляции(.с файлах) определена ГЛОБАЛЬНАЯ переменная const byte Sand_char[]

Тоесть на выходе будут два .obj файла с такой перменной с двумя разными адресами в памяти.
Линкер хочет при упоминании Sand_char найти адрес и подставить, но не может т.к. есть два кандидата с одинаковыми именами.

И такая ситуация у вас там похоже по всем ошибкам. Как только вы поймете их природу и всё то, что я вам описал по поводу процесса сборки проекта - сможете пофиксить.

Ситуацию с Sand_char можно разрулить несколькими способами:
1. добавить static
Код
static const byte Sand_char[] PROGMEM={0,27,0,4,0,14,17,0}; //Загружаемый символ грусти

Таким образом каждый .c файл себе заинклюдит этот .h файл и получит статик, область видимости которого ограничена одной единицей трансляции(т.е. только этим компилируемым .с файлом).
2. У вас уже есть файл Symbol.c. Отлично!
Перемещаем вот это
Код
const byte Sand_char[] PROGMEM={0,27,0,4,0,14,17,0}; //Загружаемый символ грусти
в Symbol.c
А в Symbol.h пишем
Код
extern const byte Sand_char[];
Я сейчас сходу не уверен как это скомпилится, возможно надо будет по-танцевать с прогмэмом, но смысл этого всего в следующем:
Переменная глобальная переменная Sand_char будет экспортирована только из одного .obj файла(Symbol.obj) а все остальный .c файлы которые сделают #include "Symbol.h" - получат представление о том, что есть такая переменная описанная так-то, но сама эта переменная определена где-то в другом модуле(слово extern). Таким образом у линкера будет только одна копия Sand_char и он сможет подставить ее адрес везде где она используется.

Второй способ мне в данном случае нравится больше и кажется более корректным.
ArtemKAD
В хидеры включены переменные(и константные переменные в т.ч.), что ошибочно. Всем объявлениям(но не extern-прототипам) переменных, а тем более объявлениям которые используются исключительно в пределах одного файла место только в .с-файле. Если есть желание какую либо переменную сделать глобальной(использовать в нескольких файлах), её прототип в .h-файле надо объявить как extern . Если глобальной хочется сделать структуру, в .h-файле объявляется её typedef, там-же объявляется прототип переменной через extern, а сама переменная структуры и её инициализация объявляется уже в .с-файле.

ЗЫ. Кстати, PROGMEM это макрос указывающий атрибут переменной поэтому как по мне лучше его объявить до имени переменной.
const byte PROGMEM Home_char[] ={4,10,17,31,21,17,31,0}; //Загружаемый символ домика
или даже так:
PROGMEM const byte Home_char[] ={4,10,17,31,21,17,31,0}; //Загружаемый символ домика
Тогда меньше непоняток в написанном возникает.
Baser
Цитата(Smoky @ Jan 1 2018, 21:40) *
Прошу посмотреть фрагмент программы и сказать что я делаю не так.

Студии у меня нет, посмотрел глазами.
С функциями уже хорошо.
Но переменные объявляете не правильно.

1) Все объявления переменных, по которым компилятор выделяет память, должны находится в начале .c файлов.
В .h файлах помещаются только разные макросы и определения.

То есть, всякие:
volatile word sign_clcdur=0;
двигаем в .c файл.

Если эта переменная используется только в нем, больше ничего не нужно.
Если она применяется еще в других файлах, тогда в соответствующий .h добавляется:
extern volatile word sign_clcdur;

И этот .h файл включается через #include туда, где применяется.

2) Структуры объявляются через typedef в .h файлах

typedef volatile struct //Структура индексов
{
bit level:1; //Индекс текущего уровня звукового порта
bit waiting:1; //Индекс ожидания готовности к формированию звукового сигнала
} insg_t;

extern insg_t insg;

Само определение структуры, по которому выделяется память и проводится инициализация, в .c файле:
insg_t insg = {0,0};

3) Порядок нескольких #include имеет значение, т.к. компилятор работает последовательно,
он не знает определений, которые будут "ниже", "позже".
Dog Pawlowa
Цитата(sigmaN @ Jan 1 2018, 23:01) *
...
Ситуацию с Sand_char можно разрулить несколькими способами:
1. добавить static
...

Так можно ошибки разрулить, а ситуацию только усугубить.
Наверняка автор предусматривал, что это одна и та же переменная.
Первый способ создаст две переменных, ошибок не будет, но программ не будет работать.
sigmaN
Цитата
Первый способ создаст две переменных, ошибок не будет, но программ не будет работать.
Нет, в данном конкретном проекте это не станет проблемой.
НО так как этот static способ не совсем правильный я внизу своего сообщения добавил:
Цитата
Второй способ мне в данном случае нравится больше и кажется более корректным.
Smoky
Цитата(Baser @ Jan 2 2018, 02:30) *
Студии у меня нет, посмотрел глазами.
С функциями уже хорошо.
Но переменные объявляете не правильно.

1) Все объявления переменных, по которым компилятор выделяет память, должны находится в начале .c файлов.
В .h файлах помещаются только разные макросы и определения.


Коллеги, вы "ломаете" все мои установки! Программировать начал с 2009 г. , оказалось что это лишь мои "вершки". Попробую всё сначала, спасибо за ценные советы. Предлагаю для анализа файл сообщений который выводит компилятор AVRStudio.
Baser
Цитата(Smoky @ Jan 2 2018, 19:11) *
Предлагаю для анализа файл сообщений который выводит компилятор AVRStudio.

Коряво как-то Студия лог сборки выводит, ну да и ладно.
Там у вас все время мелькает : "multiple definition of ..."

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

Сверьтесь с моими рекомендациями выше.

Для лучшего понимания подсказка:
Определения, по которым НЕ выделяется память, и которые могут быть в .h файлах:

#define
enum
typedef
extern
прототипы функций - типа void func(void);

все остальное - в .c файлы
.c файлы пока друг в друга через #include не включайте (это можно, но нужно понимать, что вы делаете).

з.ы. еще может быть переопределение одних и тех же #define в разных местах, тоже будет ругаться.
sigmaN
Цитата
Коллеги, вы "ломаете" все мои установки! Программировать начал с 2009 г
А разницы нет с какого года. При правильно подходе я думаю и за год можно знать и применять 98% всех свойств языка Си.
В сравнении с С++ там вообще всё просто.
Предполагаю, что не с того вы начали... Просто видимо набрали пару примеров, сформировали у себя какие-то представления и решали поставленные задачи исходя из этого представления. Без особого понимания что к чему.
Вам бы начать изучение как положено с книжечек по языку. А так то и стандарт языка полистать было бы очень неплохо(но это уже на более поздних этапах будет уместно).

Не думаю, что по Си придумали что-то лучшее чем сами авторы языка(за качество перевода не отсечаю, просто нагуглил)
http://www.nsu.ru/xmlui/bitstream/handle/nsu/9058/kr.pdf

В понимании процесса сборки вам надо бы полистать документацию компилятора и линкера.
Непонятные слова и выражения тут-же гуглить и пополнять свои знания.
Ну может быть начните с документации GCC, я не знаю...
https://gcc.gnu.org/onlinedocs/
А может и что-то более удобоваримое можно нагуглить.. В любом случае вам нужно начинать с основ.
Smoky
Цитата(sigmaN @ Jan 3 2018, 03:30) *
Вам бы начать изучение как положено с книжечек по языку. А так то и стандарт языка полистать было бы очень неплохо(но это уже на более поздних этапах будет уместно).


Да вы правы, как я уже говорил, всё придётся повторить сначала... Я и начал в 2009 году с изучения "С за 21 ден" Брэдли Л.Джонса и Питера Эйткена. Но моя беда в том что все примеры в этой книге основаны на одном файле .c и stdio.h. Поэтому, я думаю, понятно, почему я всё это время тоже использовал один файл .c и "кучу" заголовочных файлов. Всем спасибо за ценные советы и ссылки. Далее я попробую сам...
Владивольт
похожая тема - взгляните
Smoky
Моя благодарность "волхвам" за науку, всё получилось так как нужно. Ещё раз примите моё спасибо и пожелания удачи. biggrin.gif
sigmaN
Обращайтесь, я по умничать всегда рад wink.gif
Unfog
Для исключения повторной компиляции содержимого h-файла несколько раз еще можно использовать следующие приемы:

Код
#ifndef DEFINE_H_
#define DEFINE_H_

int i = 0;

#endif /*DEFINE_H_*/

или
Код
#pragma once
Smoky
Цитата(Unfog @ Jan 12 2018, 12:13) *
Для исключения повторной компиляции содержимого h-файла несколько раз еще можно использовать следующие приемы:

или
Код
#pragma once


А это в каком компиляторе?
Grizzzly
Цитата(Smoky @ Jan 12 2018, 09:49) *
А это в каком компиляторе?

Ну очень во многих: https://ru.wikipedia.org/wiki/Pragma_once
Kabdim
Цитата(Smoky @ Jan 12 2018, 09:49) *
А это в каком компиляторе?

Настолько во всех что я до сих пор удивляюсь что её не внесли в стандарт.
Baser
Цитата(Kabdim @ Jan 12 2018, 10:21) *
Настолько во всех что я до сих пор удивляюсь что её не внесли в стандарт.

ИАР значится в табличке в Википедии, однако в тех версиях (не очень старых) что я использую для MSP430 и STM32,
#pragma once отсутствует.

Есть только упоминание о ней в главе: Recognized pragma directives (6.10.6)
In addition to the pragma directives described in the chapter Pragma directives, the
following directives are recognized and will have an indeterminate effect.
Smoky
Цитата(Grizzzly @ Jan 12 2018, 13:04) *
Ну очень во многих: https://ru.wikipedia.org/wiki/Pragma_once


Проверил в AVR Studio, работает. Спасибо.
aiwa
Цитата(Kabdim @ Jan 12 2018, 10:21) *
Настолько во всех что я до сих пор удивляюсь что её не внесли в стандарт.


Точнее, настолько во всех, что его не смогли выбросить.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.