Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Мучитель микросхем
Аватара пользователя
Сообщения: 424
Зарегистрирован: Сб авг 25, 2007 22:02:05
Откуда: Германия, Viernheim

Сообщение unalex »

aam писал(а):Короче, я так понял. Переменные я вс-таки должен объявлять ("unsigned char a;") именно в соответствующем сишнике (RC5.c). Тогда те из переменных, которые я хочу использовать другими модулями (файлами) проги, я должен дополнительно описать ("extern unsigned char a;") в h-файле. Те, которые сугубо личные и юзаются только внутри библиотеки - соответственно не описываю. Так?
операции с переменными
- объявление - указываем имя переменной и ее тип
- определение - выделяем для переменной память

переменная может иметь
- внешние связи - доступны из любой части программы(глобальные переменные)
- внутренние связи - доступны только внутри файла(глобальная со словом static)
- нет связей - локальная(видима только внутри функции и исчезающия при выходе из нее)

Код: Выделить всё

//main.c (или projectname.c, кому как нравится)
...
#include "module.h"
...
//объявление и определение глобальной переменной
//указываем имя и тип и выделяем память
//может использоваться другими файлами(имеет внешние связи)
int a;

//объявление и определение глобальной переменной
//указываем имя и тип и выделяем память
//не может использоваться другими файлами(имеет внутренние связи)
static int b;

int main()
{
	//объявление и определение локальной переменной
	//указываем имя и тип и выделяем память
	//доступна только в main() (не имеет связей)
	int c;

	c = blabla();
}

Код: Выделить всё

//module.h
#ifndef MODULE_H
#define MODULE_H

int blabla();

#endif

Код: Выделить всё

//module.c

//объявление(но не определение) глобальной переменной
//память для нее уже выделена в другом файле
//нужна чтобы получить доступ к переменной, определенной в другом файле
//а также чтоб не возникало ошибок при раздельной компиляции
extern int a;
...
	
int blabla()
{
	//объявление и определение локальной переменной
	//указываем имя и тип и выделяем память
	//доступна только в blabla() (не имеет связей)
	int d;
	...
	return d;
}
Коктейль "Рекурсивный": 20% спирта, 30% воды, 50% коктейля "Рекурсивный"...
Реклама
aam
Собутыльник Кота
Аватара пользователя
Сообщения: 2994
Зарегистрирован: Сб фев 20, 2010 14:00:12
Откуда: Москва

Сообщение aam »

ploop писал(а):Правильнее делать функции, принимающие и возвращающие значения или указатели на всяческие структуры. А сами эти, структуры например, описаны в h-файле, объявлены в с-файле.
Народ, не забывайте, мы не о компе говорим :)) И даже не о линуксе на плате))
В принципе, я все это понял - на компе я бы объявил класс на Си++ и не парился. Но когда речь идет о процах Тини и Мега (причем младших Мегах типа 8-й) какой может быть базар о каких-то доп. функциях get/set, когда каждый байт стараешься экономить?
Вообще, это все хорошо, но как на ПРАКТИКЕ ПРИНЯТО писать софт под эти процы? Понятно, что там классы никто не забабахивает.
Мне это интересно не только с точки зрения радиолюбителя, но и с точки зрения профессионала-разработчка МК-устройств, поэтому хочу привыкнуть правильно кодить контроллеры.
BCluster писал(а):то никакой гарантии что blabla будет виден из других модулей нет. В большинстве компиляторов виден. Но, например в IAR MSP430 это происходит через раз.
Ну вот и договорились... Т. е. через глобальные переменные мне не передать результатов?
Вот та же библиотека RC5 у меня хранит результат в глобальном Юнионе со структурой внутри. И код кнопки и устройства я дергаю в основной проге именно из этой структуры. Получается что я должен написать к ней функцию get, т. е. фактически метод доступа к компонентам класса выходит. И так на все??? Так никакой Тиньки не хватит. Не может быть чтоб их ТАК программировали! Но в то же время врят ли серьезные программисты кодят их с грубыми нарушениями правил.

Получается, что на модули можно разбивать только если очень приспичило, а выделить из основной проги модули типа "меню", "функция1", "функция2", которые тесно общаются между собой через общие глобальные переменные, не выйдет?
BCluster писал(а):сли англ позвоняет
Не позволяет :oops: Даташиты еще читаю, а долго осознанно читать книжку не смогу.
Я в институте Подбельсокго читал (не за один проход кстати :))) ) А кодили мы там на Борланд 3.11, т. е. для компа.
YS писал(а):Такая конфигурация вызовет ошибку
Вот она и вызвала :))
Реклама
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

а вы не экономьте каждый байт - эта экономия выходит всегда боком. Программа 100 раз переделывается под хотелки заказчика - и каждый раз переписывать код из-за чересчур сильных связей - замучаешься.
Что смущает в варианте объявить статиком переменную в одном файле и через гет оттуда забрать из другого файла ?

Код: Выделить всё

foo.c
static char  cRez=0;

char fnGetVar(void)
{
return cRez;
}

foo.h
extern char fnGetVar(void);
Что здесь такого страшного ?
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 848
Зарегистрирован: Ср мар 02, 2011 07:47:39
Откуда: Уфа

Сообщение Psych »

aam писал(а):В принципе, я все это понял - на компе я бы объявил класс на Си++ и не парился. Но когда речь идет о процах Тини и Мега
Кстати на С++ никто вас не заставляет писать через ЖОПП. Я пишу на С++ для МК не используя классы, зато есть другие полезные плюшки.
aam писал(а): когда каждый байт стараешься экономить?
Нынче не так дорого байты стоят.
aam писал(а): поэтому хочу привыкнуть правильно кодить контроллеры.
Судя по вашим запросам вы хотите накатать супернезависимую библиотеку, которая делает всё без лишних телодвижений. А в мэйне хотите типа этого:

Код: Выделить всё

int main(){
sdelat_vse_pri_vse();
}
Не стоит тесно связанные с аппаратной частью функции (да еще и дефайны) забивать в модули. В мейне же должно быть что-нибудь. Бывает и большую по весу прогу на отдельные логические части не разобьешь, хотя конечно разбить можно всё что угодно---вот только надо ли это, если вы будете постоянно лазить по файлам и менять там что-то.

Есть программисты, а есть разработчики библиотек. Поизучайте как сделаны библотеки профами.
urry писал(а):Что смущает в варианте объявить статиком переменную в одном файле и через гет оттуда забрать из другого файла ?
Вот тебе и инкапсуляция, вот тебе и функция доступа :))
Реклама
Эиком - электронные компоненты и радиодетали
Модератор
Аватара пользователя
Сообщения: 13490
Зарегистрирован: Ср ноя 26, 2008 16:34:25
Откуда: Тамбовская обл.

Сообщение ploop »

aam писал(а):Народ, не забывайте, мы не о компе говорим И даже не о линуксе на плате))
В принципе, я все это понял - на компе я бы объявил класс на Си++ и не парился. Но когда речь идет о процах Тини и Мега (причем младших Мегах типа 8-й) какой может быть базар о каких-то доп. функциях get/set, когда каждый байт стараешься экономить?
Да при чём тут ООП? Кроме, разве что, названий идентификаторов, начинающихся на set/get. Ну обзовите их vpendurit/poimet, чтобы не смущало :))) Передача указателя стандартная практика даже на ассемблере, только называют их напрямую - адресами.

И можете не переживать, компиляторы нынче умные, и грамотно составленный проект с правильной архитектурой они слинкуют так, как руками на асме набыдлокодить не выйдет.
Реклама
aam
Собутыльник Кота
Аватара пользователя
Сообщения: 2994
Зарегистрирован: Сб фев 20, 2010 14:00:12
Откуда: Москва

Сообщение aam »

Psych писал(а):Судя по вашим запросам вы хотите накатать супернезависимую библиотеку, которая делает всё без лишних телодвижений. А в мэйне хотите типа этого
Нет, я всего-навсего хочу понять как ПРАВИЛЬНО кодить микроконтроллеры. При этом правильно без дури и с желанием иметь код минимального объема.
Или может тут дела обстоят как с начертанием схем, где ГОСТ читается кому как в голову взбредет? Ато у меня на работе была одна такая "чоткая" - так схемы после нее понять без пол литра водного раствора этанола невозможно :beer: Но возможности ГОСТа раскрыты почти полностью :)))

Насчет разбиения на модули - я думал что выделять некоторые куски кода в отдельные файлы - типа правило хорошего тона, т. к. гонять туда-сюда 1 сишник на 100 страниц как-то не очень удобно. Но может я ошибся...
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Вообще, это все хорошо, но как на ПРАКТИКЕ ПРИНЯТО писать софт под эти процы? Понятно, что там классы никто не забабахивает.
На практике каждый дро пишет как хочет и как ему представляется наиболее красивым и правильным. В эмбеде (пока?) нет каких-то более-менее жестких специальных стандартов оформления кода, хотя ST и пытается пропихнуть свой (одна из причин того, что политику ST я недолюбливаю).

Кстати, ардуйня вполне использует классы и ООП. Код получается дичайше неэффективным - простая перекидывалка данных с Bluetooth-шилда на I2C с минимальными проверками занимала у меня больше 7 кБ на ардуино, но гуманитариям (а также, иногда, программистам-прикладникам) нравится, потому что при ардуиновом подходе требования к погружению в предметную область снижаются до нуля. Так что все зависит от того, чего мы хотим достичь. :)

ОК, давайте я раскажу как гляжу на это я. :)

1. В смысле принципов написания кода под встроенные системы следует руководствоваться правилами MISRA C. Может показаться, что их дофига, но на самом деле это не страшно - большинство из них просто концентрированный здравый смысл.

2. О модулях. Если проект немного сложнее мигалки, то я предпочитаю выносить в отдельные модули (.c + .h) логически законченные части программы. Например HAL, реализации интерфейсов, слои общения с пользователем и т.п.

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

3. Касаемо взаимодействия. До Get/Set я пока не дошел, хотя Init() частенько делаю. Для коммуникации с модулем я, бывает, применяю глобальные переменные с набором флагов. При этом стараюсь свести количество торчащих из модуля переменных к минимуму.
некоторые куски кода
Логически завершенные куски кода. :idea:
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

aam писал(а):Ну вот и договорились... Т. е. через глобальные переменные мне не передать результатов?
Я этого не говорил. Я говорил о том, что объявить переменную надо в каждом .с, где она используется. Если она в .h объявлена как extern, то это будет одна и та же глобальная переменная.
aam писал(а):Народ, не забывайте, мы не о компе говорим И даже не о линуксе на плате))
Что вы прицепились к этой ерунде, и пишете ее в каждом посте. Кроилово каждого байта приводит к архитектурным ошибкам в проекте, и делает его совершенно непригодным для дальнейших модификаций. Я уже писал ранее, покажете код. Я думаю что там найдется куча вещей, которые можно оптимизировать без ущерба.
Вообще я стараюсь делать правильно сразу - если что-то где-то не влазит, тогда уже можно подумать о том, чтобы вставлять костыли. Но таких случаев раз два и обчелся.
Функции доступа к переменным занимают очень мало места, поэтому это не тот случай, где сильно съэкономится пространство.
aam писал(а):При этом правильно без дури и с желанием иметь код минимального объема.
Проведите эксперимент. С глобальными переменными и с функциями доступа. Уверен, результаты будут не такие, как вы ожидаете.
YS писал(а): В смысле принципов написания кода под встроенные системы следует руководствоваться правилами MISRA C.
Вот это хороший совет.
YS писал(а):ардуйня вполне использует классы и ООП
Я тоже использую классы и ООП в МК. Никто не умер, а в большом проекте очень хорошо помогает создать нормальную архитектуру. МК msp430 512kFLASH 32kRAM...
YS писал(а):простая перекидывалка данных с Bluetooth-шилда на I2C с минимальными проверками занимала у меня больше 7 кБ на ардуино
хотел бы глянуть, если можно, действительно жирновато :)
Я просто не вижу причин, почему ООП код должен быть намного больше по объему чем ПП-код. ООП оставляет вопросы по производительности, из-за полиморфного поведения объектов, в частности, и глубокого наследования. Но это совсем другая история.
Контактная информация:
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

МК msp430 512kFLASH 32kRAM...
Мы-то говорим о совсем мелких МК. Когда столько памяти, так ООП это уже не такой грех... Да и проект у вас - явно не перекидывалка данных. :)
хотел бы глянуть, если можно, действительно жирновато
Напишу в ЛС.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

YS писал(а):Мы-то говорим о совсем мелких МК.
Я понимаю, я просто выразил точку зрения о том, что нормальный ООП незначительно больше по объему чем ПП-код :)
Контактная информация:
Мучитель микросхем
Аватара пользователя
Сообщения: 424
Зарегистрирован: Сб авг 25, 2007 22:02:05
Откуда: Германия, Viernheim

Сообщение unalex »

aam писал(а):Вот та же библиотека RC5 у меня хранит результат в глобальном Юнионе со структурой внутри. И код кнопки и устройства я дергаю в основной проге именно из этой структуры. Получается что я должен написать к ней функцию get, т. е. фактически метод доступа к компонентам класса выходит. И так на все??? Так никакой Тиньки не хватит
а с чего вы взяли что она должна хранить результат в глобальном пространстве?
сделайте его локальным, тратя на нее память, только когда она необходима

Код: Выделить всё

//main.c
#include "module.h"

int main (void)
{
	params firstCircuit, secondCircuit;//объявляем и выделяем память

	firstCircuit.voltage = 5;
	firstCircuit.current = 0.02;

	secondCircuit = change(firstCircuit);

	return 0;
}

Код: Выделить всё

//module.h

#ifndef MODULE_H_
#define MODULE_H_

typedef struct
{
	int voltage;
	double current;
}params;//объявляем, но память не резервируем

params change(params circuit);

#endif /* MODULE_H_ */

Код: Выделить всё

//module.cpp
#include "module.h"

params change(params circuit)
{
	circuit.voltage = 10;
	circuit.current = 0.01;
	return circuit;
}
заметьте, что память выделяется в main.c, а функция в module.c памяти не потребует, она лишь ссылается на область пямяти, выделенную в стеке при вызове этой функции

ктати никаких get не потребовалось
Коктейль "Рекурсивный": 20% спирта, 30% воды, 50% коктейля "Рекурсивный"...
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

unalex писал(а):ктати никаких get не потребовалось
А причем тут get?
Вообще речь шла о том, что не стоит использовать глобальные переменные. А модуль требует отдавать и получать некоторые значения. Ваш код не решает ни одной из этих задач. Кроме того, он не оптимален. Зачем передавать структуру по значению? Гораздо правильнее было бы передавать указатель на структуру. Тогда бы не пришлось хранить в стеке копию структуры.
Тогда бы все свелось к такому:

Код: Выделить всё

void change(params * circuit)
{
   circuit->voltage = 10;
   circuit->current = 0.01;  
}

Код: Выделить всё

change(secondCircuit);
Кстати, в вашем примере можно не передавать вообще ничего в качестве параметра, а просто определить в функции новую структуру. Что будет равносильно.
Контактная информация:
Мучитель микросхем
Аватара пользователя
Сообщения: 424
Зарегистрирован: Сб авг 25, 2007 22:02:05
Откуда: Германия, Viernheim

Сообщение unalex »

BCluster писал(а):Вообще речь шла о том, что не стоит использовать глобальные переменные
именно об этом я и вел речь

ну а пример выбран тривиальным, чтоб просто показать, что глобальных переменных не требуется, и никаких проблем с зависимостями и переопределениями в разных файлах не возникает, и естественно обратив внимание на тот факт что расходы памяти в таком случае не будут огромными, чего так боится коллега
Коктейль "Рекурсивный": 20% спирта, 30% воды, 50% коктейля "Рекурсивный"...
aam
Собутыльник Кота
Аватара пользователя
Сообщения: 2994
Зарегистрирован: Сб фев 20, 2010 14:00:12
Откуда: Москва

Сообщение aam »

Глянул DATA в своей проге - больше 100 байт! Может как раз из-за глобальных переменных?)
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

Вероятнее всего так. Вы ж не хотите показать, что за гениальность ваяете :)))
У нас сейчас проект на МК у которого 256кБ флэш, и 2(!!!) кБ ОЗУ :) Вот там мда ) Если учесть что флэш под завязку забит.
В принципе все решается методом искусственного хипа, если кому будет интересно - расскажу
Контактная информация:
aam
Собутыльник Кота
Аватара пользователя
Сообщения: 2994
Зарегистрирован: Сб фев 20, 2010 14:00:12
Откуда: Москва

Сообщение aam »

BCluster писал(а):В принципе все решается методом искусственного хипа, если кому будет интересно - расскажу
Типа new/ freе ? Но в чем фишка?
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

Хотя я в Си без году неделя (тем более, после асма - Си трудновато дался) но все таки, какие никакие, а результаты есть.....
Так вот, как то один уважаемый человек мне рассказал, как он пишет на Си, тут - http://radiokot.ru/forum/viewtopic.php? ... д#p1338777
никаких глобальных переменных, все данные спрятаны внутри модулей, все обращения к ним - только через функции.
И хотя о совершенстве пока речи нет, но я именно в этом направлении все время иду, и очень рад что в самом начале освоения Си, получил пинок в правильном направлении .....
aam писал(а):Т. е. через глобальные переменные мне не передать результатов?
Именно!
Разбиваеш программу на модули. Я делаю например так (для примера DDS генератор):

Главный файл (вся высокоуровневая логика)
Глобальные переменные: Частота, режим работы, позиция курсора, байт флагов....ВСЕ! Остальные переменные со статиками внутри функций.
Отдельными модулями подключены LCD.h, AD9833.h, ENCODER.h

В каждом модуле - минимум глобальных переменных...

Общение с модулями идет только через обертки... (единственное где я обращаюсь напрямую из майна к глобальной области модуля - это буфер LCD дисплея)
Так вот, это только так кажется что общение через обертки - получается громоздко... На сам деле, компиль не глупый, так что там все минимально...

В результате имеем хоть и не С++ но тем не менее, довольно абстрактный код в майне, что делает программирование довольно комфортным, минимально трудоемким.
А модули максимально обособлены и легко подключаются к новым проектам...
aam писал(а):Получается что я должен написать к ней функцию get, т. е. фактически метод доступа к компонентам класса выходит. И так на все??? Так никакой Тиньки не хватит.
Если поставить цель максимально минимизировать, то тогда вам нужен асм... Си однозначно подразумевает лишние расходы ресурсов, но для их минимизации есть много нюансов, которые постигаются с опытом... тут например кое что обсуждалось http://radiokot.ru/forum/viewtopic.php?f=57&t=77142
По крайней мере уже нет смысла пихать чтото в мелкашку, т.к. более жирные МК стоят не дороже...
aam
Собутыльник Кота
Аватара пользователя
Сообщения: 2994
Зарегистрирован: Сб фев 20, 2010 14:00:12
Откуда: Москва

Сообщение aam »

shads писал(а):По крайней мере уже нет смысла пихать чтото в мелкашку, т.к. более жирные МК стоят не дороже...
А когда из 32-х ног Меги8 использовано только 10 - это типа круто и есть смысл? :))
Но либо я чего-то не понял, либо кодить не умею, но простейшая девайсина с пультом от телека и БЕЗ знаковых/сегментных индикаторов у меня в Тиню2313 не лезет.

К слову, делал зарядник для АА (там Мега32, 4 канала программных IBV-стабилизаторов тока, нефиговый алгоритм детектирования конца зарядки и ЖКИ 8х2 с нехилым меню) - заняло всего 25% флеша...


Скажите, а регистр флагов библиотеки тоже через get/set вытаскивать надо?
И вот если в пределах одного файла, скажем, основной программы, как без глобальных переменных я обойдусь? Ведь разные функции используют общие для всей программы переменные. Т. е. все равно от глобальных переменных никуда не деться.
Вымогатель припоя
Аватара пользователя
Сообщения: 535
Зарегистрирован: Вт авг 28, 2012 22:21:33

Сообщение menzoda »

aam писал(а):И вот если в пределах одного файла, скажем, основной программы, как без глобальных переменных я обойдусь? Ведь разные функции используют общие для всей программы переменные. Т. е. все равно от глобальных переменных никуда не деться.
Они будут не глобальными, а "локальными" для модуля, и без них по понятным причинам не обойтись, если конечно модуль не представляет собой набор независимых функций (к примеру математических). Модуль на Си - это как-бы класс (возможно статический), статические переменные и функции модуля - как-бы приватные члены класса, в этом ничего плохого нет и избавляться от них не нужно.
Последний раз редактировалось menzoda Пт янв 24, 2014 10:17:20, всего редактировалось 2 раза.
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

Во первых, деться от них можно. Для этого надо всего лишь передавать в качестве параметров в другие функции необходимые данные, либо использовать static внутри функций.
Во вторых, использование ВНУТРИ МОДУЛЯ некоторого количества static переменных вполне допустимо.
shads писал(а):В каждом модуле - минимум глобальных переменных...
Глобальная переменная это extern, видимая из всей программы. Статик-переменные внутри модуля это совсем другая песня. Тут речь идет о межмодульных взаимодействиях.

ЗЫ: Кстати, aam, вы в курсе как работают static-переменные внутри функций?
Контактная информация:
Ответить

Вернуться в «Разные вопросы по МК»