Перейти к содержанию
    

Заголовочные файлы и модули.

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

Переделка только одного файла может только запутать все эти перекрестные ссылки.

Нужно переделывать все. Обычно это довольно трудоемко.

У меня есть опыт перенятия чужих проектов начинающих программистов, которые плохо понимали структуризацию проектов на Си.

На "распутывание" проекта из ~20 файлов уходила пара дней :(

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

но это вам можно оставить на потом, после вникания в это.

 

Логику использования .h файлов я понял, я не уловил в примерах закономерности директив #include в модулях .c

Для начала, все .с файлы должны быть включены в проект в самой IDE AVRStudio

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

 

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

И так по всем ошибкам компилятора.

 

С Новым Годом! :santa2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Переделка только одного файла может только запутать все эти перекрестные ссылки.

Нужно переделывать все. Обычно это довольно трудоемко.

 

Согласен. Поэтому решил начать сначала. Прошу посмотреть фрагмент программы и сказать что я делаю не так. Этот фрагмент инициализирует LCD 1х20, загружает пользовательские символы, выводит на LCD "Инициализация" и короткий звуковой сигнал 10 мсек. Файлы .c компилируются нормально но вот сборка не получается. Директивы #include вставлял по мере вывода сообщений об ошибках но результата так и не получил...

Cloc_Meteo1_2.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне пришлось импортировать проект в 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 и он сможет подставить ее адрес везде где она используется.

 

Второй способ мне в данном случае нравится больше и кажется более корректным.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В хидеры включены переменные(и константные переменные в т.ч.), что ошибочно. Всем объявлениям(но не 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}; //Загружаемый символ домика

Тогда меньше непоняток в написанном возникает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прошу посмотреть фрагмент программы и сказать что я делаю не так.

Студии у меня нет, посмотрел глазами.

С функциями уже хорошо.

Но переменные объявляете не правильно.

 

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 имеет значение, т.к. компилятор работает последовательно,

он не знает определений, которые будут "ниже", "позже".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Ситуацию с Sand_char можно разрулить несколькими способами:

1. добавить static

...

Так можно ошибки разрулить, а ситуацию только усугубить.

Наверняка автор предусматривал, что это одна и та же переменная.

Первый способ создаст две переменных, ошибок не будет, но программ не будет работать.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Первый способ создаст две переменных, ошибок не будет, но программ не будет работать.
Нет, в данном конкретном проекте это не станет проблемой.

НО так как этот static способ не совсем правильный я внизу своего сообщения добавил:

Второй способ мне в данном случае нравится больше и кажется более корректным.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Студии у меня нет, посмотрел глазами.

С функциями уже хорошо.

Но переменные объявляете не правильно.

 

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

В .h файлах помещаются только разные макросы и определения.

 

Коллеги, вы "ломаете" все мои установки! Программировать начал с 2009 г. , оказалось что это лишь мои "вершки". Попробую всё сначала, спасибо за ценные советы. Предлагаю для анализа файл сообщений который выводит компилятор AVRStudio.

OutCloc_Meteo1_2.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Предлагаю для анализа файл сообщений который выводит компилятор AVRStudio.

Коряво как-то Студия лог сборки выводит, ну да и ладно.

Там у вас все время мелькает : "multiple definition of ..."

 

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

находящиеся в разных файлах.

 

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

 

Для лучшего понимания подсказка:

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

 

#define

enum

typedef

extern

прототипы функций - типа void func(void);

 

все остальное - в .c файлы

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

 

з.ы. еще может быть переопределение одних и тех же #define в разных местах, тоже будет ругаться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Коллеги, вы "ломаете" все мои установки! Программировать начал с 2009 г
А разницы нет с какого года. При правильно подходе я думаю и за год можно знать и применять 98% всех свойств языка Си.

В сравнении с С++ там вообще всё просто.

Предполагаю, что не с того вы начали... Просто видимо набрали пару примеров, сформировали у себя какие-то представления и решали поставленные задачи исходя из этого представления. Без особого понимания что к чему.

Вам бы начать изучение как положено с книжечек по языку. А так то и стандарт языка полистать было бы очень неплохо(но это уже на более поздних этапах будет уместно).

 

Не думаю, что по Си придумали что-то лучшее чем сами авторы языка(за качество перевода не отсечаю, просто нагуглил)

http://www.nsu.ru/xmlui/bitstream/handle/nsu/9058/kr.pdf

 

В понимании процесса сборки вам надо бы полистать документацию компилятора и линкера.

Непонятные слова и выражения тут-же гуглить и пополнять свои знания.

Ну может быть начните с документации GCC, я не знаю...

https://gcc.gnu.org/onlinedocs/

А может и что-то более удобоваримое можно нагуглить.. В любом случае вам нужно начинать с основ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вам бы начать изучение как положено с книжечек по языку. А так то и стандарт языка полистать было бы очень неплохо(но это уже на более поздних этапах будет уместно).

 

Да вы правы, как я уже говорил, всё придётся повторить сначала... Я и начал в 2009 году с изучения "С за 21 ден" Брэдли Л.Джонса и Питера Эйткена. Но моя беда в том что все примеры в этой книге основаны на одном файле .c и stdio.h. Поэтому, я думаю, понятно, почему я всё это время тоже использовал один файл .c и "кучу" заголовочных файлов. Всем спасибо за ценные советы и ссылки. Далее я попробую сам...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Моя благодарность "волхвам" за науку, всё получилось так как нужно. Ещё раз примите моё спасибо и пожелания удачи. :biggrin:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для исключения повторной компиляции содержимого h-файла несколько раз еще можно использовать следующие приемы:

 

#ifndef DEFINE_H_
#define DEFINE_H_

int i = 0;

#endif /*DEFINE_H_*/

или

#pragma once

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...