Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Немного стандарта написания программ на С
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры > PIC
torik
Всем привет.

Разрабатываю программу для PIC24F. Необходимо более-менее придерживаться стандарта. У меня вопрос следующий...

Обычно все переменные я объединяю в структуры, группирую по назначению
Код
typedef struct {
  alt_buttons* buttons;                                             //ссылка на массив кнопок
  unsigned char max_key_num;                                //число кнопок в клавиатуре
  .......
  unsigned char alpha;                                             //прозрачность клавиатуры
} alt_keyboard;


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

Правильно, вроде как, считается создавать локальные переменные и передавать их затем в качестве аргумента. Например, в main:
Цитата
void main (void) {
alt_keyboard keyboard;

system_init (&keyboard);


Но вот необходимо обрабатывать прерывания. Как же быть там? Получается, нужно работать с глобальными переменными.
Это плохо? Кто как поступает?

Можно основные переменные (например, клавиатура, дисплей и т.п.) сделать глобальными и работать с ними из любой функции. Не?
Alex B._
Цитата(torik @ Oct 26 2009, 12:20) *
Правильно, вроде как, считается создавать локальные переменные и передавать их затем в качестве аргумента. Например, в main:

кем считается? первый раз такое слышу. Наоборот, если оптимизатор не отработает, то стеку будет расходоваться больше - к примеру, слово для локальной переменной и слово для передачи параметра (если через регистры все параметры передать не получится).

Цитата(torik @ Oct 26 2009, 12:20) *
Получается, нужно работать с глобальными переменными.Это плохо? Кто как поступает?

в общем случае плохо, но иногда без этого никак, смело пользуйся
MrYuran
Цитата(Alex B._ @ Oct 26 2009, 12:35) *
в общем случае плохо, но иногда без этого никак, смело пользуйся

А чем плохо?
Переменная висит статически, в заранее известном адресе, существует всё время работы.
И учитывать память проще, чем когда локальные переменные вываливаются на стек
Dima_G
Цитата(torik @ Oct 26 2009, 11:20) *


Я следую такому правилу - делать переменную глобальную только в том случае, когда без этого - никак (например, по другому никак не передать данные в/из обработчик прерываний и тд).

Где-то вычитал правило и согласен с ним. Звучит как-то так:
"Предпочитайте локальные переменные - глобальным, глобальные статические - просто глобальным"


Цитата(MrYuran @ Oct 26 2009, 12:47) *
А чем плохо?
Переменная висит статически, в заранее известном адресе, существует всё время работы.


1. именно этим и плохо. Разово нужная переменная постоянно занимает память.
2. программы с глобальными переменными очень плохо читаются (ну это мое субъективное мнение)
3. глобальная - доступна всем желающим (во всей программе, либо в единице трансляции, если это - статик). Можно посадить трудноуловимые баги smile.gif.
4, Трудность переноса кода - если функция использует только локальные переменные, ее в общем случае можно просто "взять и выдернуть". Использующая глобальные переменные функция, потянет за собой хвост этих переменных...
torik
Цитата
1. именно этим и плохо. Разово нужная переменная постоянно занимает память.
2. программы с глобальными переменными очень плохо читаются (ну это мое субъективное мнение)
3. глобальная - доступна всем желающим (во всей программе, либо в единице трансляции, если это - статик). Можно посадить трудноуловимые баги smile.gif.
4, Трудность переноса кода - если функция использует только локальные переменные, ее в общем случае можно просто "взять и выдернуть". Использующая глобальные переменные функция, потянет за собой хвост этих переменных...


С этими недостатками согласен полностью!
MrYuran
Цитата(Dima_G @ Oct 26 2009, 14:22) *
1. Разово нужная переменная постоянно занимает память.

Если разово, то согласен - локальные. Но бывают флаги/состояния, которые нужно хранить от вызова к вызову.
Не забываем также, что речь идёт о программировании контроллеров, а не ПиСи
Цитата
2. программы с глобальными переменными очень плохо читаются (ну это мое субъективное мнение)
3. глобальная - доступна всем желающим (во всей программе, либо в единице трансляции, если это - статик). Можно посадить трудноуловимые баги smile.gif.
4, Трудность переноса кода - если функция использует только локальные переменные, ее в общем случае можно просто "взять и выдернуть". Использующая глобальные переменные функция, потянет за собой хвост этих переменных...

И что, предлагаете в мэйне организовать глобальную свалку переменных со всего проекта, а в каждую функцию передавать десяток параметров?
Может, это и правильно с т.з. высоких материй (опять же применительно к PC), но в практической работе очень неудобно.

Я в процессе эволюции пришёл к созданию максимально независимых модулей со своими глобальными и статическими переменными.
Те переменные, которые надо видеть "снаружи" модуля, выношу extern-ом в h-файл модуля.
А main в таком случае состоит из подключения соответствующих модулей через h-файлы, инициализации и быстрого суперцикла.
"Взять и выдернуть" в таком случае можно не отдельную функцию, а модуль. Да и не надо его дёргать, просто подключить и использовать.
xemul
Переменная, используемая в прерывании, должна быть объявлена как volatile, что приведёт к её размещению в статической банке - здесь ничего не съэкономить.
Ну и компилятор может не переварить volatile у локальной переменной.
Касательно переноса кода - код, завязанный на прерывания, переносится ручками и глазкамиsmile.gif.
Dima_G
Цитата(MrYuran @ Oct 26 2009, 14:59) *
Не забываем также, что речь идёт о программировании контроллеров, а не ПиСи

А в чем разница? smile.gif


Цитата(MrYuran @ Oct 26 2009, 14:59) *
И что, предлагаете в мэйне организовать глобальную свалку переменных со всего проекта, а в каждую функцию передавать десяток параметров?
Может, это и правильно с т.з. высоких материй (опять же применительно к PC), но в практической работе очень неудобно.

1. Причем тут глобальная свалка? Не совсем пойму, что вы имеете в виду. Можете привести короткий пример?
2. Если в функцию передается больше 3х параметров (за редким исключением) - это повод задуматься о том, правильно ли разбили задачу на функции. Или возможно стоит объединить какие-то параметры в структуры.
3. Опять же - причем тут ПС? Или вы считаете, что хороший стиль на ПС и на МК отличается?
MrYuran
Цитата(Dima_G @ Oct 26 2009, 15:20) *
А в чем разница? smile.gif

biggrin.gif
Ну хотя бы в том, что иногда приходится считать (кило)байты и микросекунды. В особо запущенных случаях счёт идёт на такты...
Dima_G
Цитата(xemul @ Oct 26 2009, 15:05) *
Ну и компилятор может не переварить volatile у локальной переменной.

В первый раз слышу..
На каком компиляторе у Вас вылезла такая проблема?
Часто использовал локальные volatile переменные на многих платформах - gcc powerpc, gcc arm, gcc blackfin, VisualDSP, CodeComposer - никогда не имел с этим проблеммы smile.gif


Цитата(MrYuran @ Oct 26 2009, 15:22) *
biggrin.gif
Ну хотя бы в том, что иногда приходится считать (кило)байты и микросекунды. В особо запущенных случаях счёт идёт на такты...


Ну я понимаю smile.gif
Более того, иногда и для PC (кстати, а что в вашем понимании PC? Blackfin с uClinux в него входит? А PowerPC c Linux? rolleyes.gif ) приходится сильно оптимизировать код.

Хороший программист должен писать эффективный код, хоть на дохленьком PIC, хоть на 6 ядерном TMS64+
zltigo
Цитата(Dima_G @ Oct 26 2009, 14:37) *
Часто использовал локальные volatile переменные на многих платформах - gcc powerpc, gcc arm, gcc blackfin, VisualDSP, CodeComposer - никогда не имел с этим проблеммы smile.gif

Часто? Часто это скорее всего плохо, ибо мне известен только один случай, когда volatile применительно к локальной переменной имеет смысл, да и тот по сути есть вредительство. Не поделитесь примерами использования? Банальное любопытство мучает.
Dima_G
Цитата(zltigo @ Oct 26 2009, 15:48) *
Часто? Часто это скорее всего плохо, ибо мне известен только один случай, когда volatile применительно к локальной переменной имеет смысл, да и тот по сути есть вредительство. Не поделитесь примерами использования? Банальное любопытство мучает.


Инициализация периферии. Если этот регистр более нигде не нужен, использую его только локальной переменной

Код
class ClHardwareManager
{
  static const DWORD INIT_REG_ADDR = 0x...;
  INT8 Init()
  {
    volatile DWORD* pdwInitReg_ = reinterpret_cast<volatile DWORD*>(INIT_REG_ADDR);
    *pdwInitReg_ = ...
  }

};
xemul
Цитата(Dima_G @ Oct 26 2009, 15:37) *
В первый раз слышу..
На каком компиляторе у Вас вылезла такая проблема?
Часто использовал локальные volatile переменные на многих платформах - gcc powerpc, gcc arm, gcc blackfin, VisualDSP, CodeComposer - никогда не имел с этим проблеммы smile.gif

picc 7.xx, 8.xx. За 9.05 не скажу - не помню. 9.5 и старше к локальным volatile относятся спокойно.
ReAl
Цитата(Dima_G @ Oct 26 2009, 13:55) *
Инициализация периферии. Если этот регистр более нигде не нужен, использую его только локальной переменной
Да ну...
Тут у Вас локальной переменной есть pdwInitReg_, но она ни на грамм не volatile. Указывает на volatile, но на НЕ локальную.
Dima_G
Цитата(ReAl @ Oct 26 2009, 15:57) *
Да ну...
Тут у Вас локальной переменной есть pdwInitReg_, но она ни на грамм не volatile. Указывает на volatile, но на НЕ локальную.


smile.gif Ну да
Перепутал маленько
ar__systems
Цитата(Dima_G @ Oct 26 2009, 05:22) *
1. именно этим и плохо. Разово нужная переменная постоянно занимает память.
2. программы с глобальными переменными очень плохо читаются (ну это мое субъективное мнение)
3. глобальная - доступна всем желающим (во всей программе, либо в единице трансляции, если это - статик). Можно посадить трудноуловимые баги smile.gif.
4, Трудность переноса кода - если функция использует только локальные переменные, ее в общем случае можно просто "взять и выдернуть". Использующая глобальные переменные функция, потянет за собой хвост этих переменных...

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

Есть много нюансов о которых надо помнить при решении глобал-локал. Читаемость в общем случае будет в ущерб производительности, все зависит от того, насколько она вам критична.
alekseykoj
Можно пользоваться указателями
например вот так:
Код
typedef struct {
  alt_buttons* buttons;                                             //ссылка на массив кнопок
  unsigned char max_key_num;                                //число кнопок в клавиатуре
  .......
  unsigned char alpha;                                             //прозрачность клавиатуры
} alt_keyboard;


void* h_VarA;


void Proc1(void)
{
  h_VarA->alpha = 123;  
}


main()
{
  alt_keyboard keyboard;

  system_init (&keyboard);

  h_VarA = (void*)&keyboard;

  Proc1();
}

Но в этом случае надо следить за указателем. Чтоб к нему не было обращений, когда он еще не инициализирован или динамическая структура уже освобождена
Сергей Борщ
Цитата(alekseykoj @ Dec 14 2009, 08:35) *
Можно пользоваться указателями
Можно, но какой смысл? Заводим локальную переменную в main() и через указатель делаем ее как-бы глобальной. Какие преимущества это дает по сравнению с обычной глобальной переменной? Т.е. ради чего занимается лишняя память под указатель и ради чего мы вынуждаем процессор постоянно обращаться к структуре косвенно?
Цитата(alekseykoj @ Dec 14 2009, 08:35) *
например вот так:
Вот так делать не нужно ни в коем случае. Вместо того, чтобы завести указатель на нужный тип и позволить компилятору контролировать ваши описки, вы заводите указатель на void * и вынуждены при каждом обращении вручную, принудительно приводить его к нужному типу. Компилятор не имеет возможности проверить, совместим ли объект, на который указывает указатель и тот тип, к которому вы его приводите методом грубой силы.
Цитата(alekseykoj @ Dec 14 2009, 08:35) *
Код
void Proc1(void)
{
  h_VarA->alpha = 123;  
}
Не скомпилится. h_VarA - указатель на void
Цитата(alekseykoj @ Dec 14 2009, 08:35) *
Код
  h_VarA = (void*)&keyboard;
Явное приведение лишнее. Любой не-cv (const и/или volatile) указатель приводится к void * неявно.

P.S. используйте кнопку на форме ввода для оформления кода.
alekseykoj
Цитата
Можно, но какой смысл? Заводим локальную переменную в main() и через указатель делаем ее как-бы глобальной. Какие преимущества это дает по сравнению с обычной глобальной переменной? Т.е. ради чего занимается лишняя память под указатель и ради чего мы вынуждаем процессор постоянно обращаться к структуре косвенно?

Смысла ни какого. И так делать не надо. Просто если очень хочется... Глобальной она все равно не будет, т.к. эта переменная уничтожится при выходе из функции (если рассматриваем вариант не с функцией main). Вообще, для этих целей есть операторы new и delete. Которые позволяют контролировать выделяемые ресурсы в момент работы программы.
Правильно будет так:

Код
typedef struct {
alt_buttons* buttons; //ссылка на массив кнопок
unsigned char max_key_num; //число кнопок в клавиатуре
.......
unsigned char alpha; //прозрачность клавиатуры
} alt_keyboard;


keyboard* h_VarA;

main()
{
// Выделяем ресурсы
  h_VarA = new alt_keyboard;

// Работаем с выделенными ресурсами....

// Освобождаем ресурсы
  delete h_VarA;

// Работаем дальше...

}


Преимущества перед глобальной созданной статически в том, что я сам контролирую выделенные ресурсы и в любой момент могу освободить память для размещения другой структуры.
OLEG_BOS
Цитата(alekseykoj @ Dec 14 2009, 11:56) *
Вообще, для этих целей есть операторы new и delete. Которые позволяют контролировать выделяемые ресурсы в момент работы программы.

Если мне не изменяет склероз, то операторы динамического распределения памяти new и delete отсутствуют в С и есть только в С++ , а топик вроде был как о С smile.gif
alekseykoj, а для каких Вы PIC-контроллеров пишете на С++? smile.gif
alekseykoj
Извиняюсь blush.gif А так?

Код
typedef struct {
alt_buttons* buttons; //ссылка на массив кнопок
unsigned char max_key_num; //число кнопок в клавиатуре
.......
unsigned char alpha; //прозрачность клавиатуры
} alt_keyboard;


alt_keyboard* h_VarA;

main()
{
// Выделяем ресурсы
  h_VarA = malloc(sizeof(alt_keyboard));

// Работаем с выделенными ресурсами....

// Освобождаем ресурсы
  free(h_VarA);

// Работаем дальше...

}
torik
Стрелять-колотить. Хоть динамическая хоть статическая, сути это не меняет. Она может быть глобальная или нет. Речь шла о противопоказаниях в применении глобальных переменных.
Я хочу сделать переменную глобальной, чтобы для нее весь проект был "прозрачен", чтобы из нескольких различных подпрограмм к ней был доступ. А переменная для работы с клавиатурой (что следует из ее названия) не должна быть динамически созданной/удаленной...
alekseykoj
Ну и делай ее глобальной. Ни каких "противопоказаний" нет. Единственное "противопоказание" следить за корректностью значения в переменной. (она же глобальная... Может быть изменена из нескольких модулей программы). Необходимо оформлять программу таким образом чтоб другие модули получали значение переменной или изменяли его с помощью функций того модуля которому "принадлежит" эта переменная. Тогда и проблемы с переносом модуля в другие программы отпадут сами собой.
OLEG_BOS
Цитата(torik @ Dec 14 2009, 15:49) *
Она может быть глобальная или нет.

Согласен с alekseykoj - если Вам нужно глобальную переменную, так делайте ее. Если возможность обойтись без глобальной переменной лучше без нее обойтись smile.gif
В одном модуле .с- файла объявляете и инициализируте глобальную переменную а в .h файле этого же модуля объявляете ее как extern. И подключаете .h файл модуля к другим модулям где переменная используеться.
Единственным "противопоказанием" может быть использование этой переменной в функции обработчике прерывания, в таком случае глобальной переменной нужно еще и volatile добавить при ее обявлении в с. файле модуля
torik
Цитата
Единственное "противопоказание" следить за корректностью значения в переменной. (она же глобальная... Может быть изменена из нескольких модулей программы). Необходимо оформлять программу таким образом чтоб другие модули получали значение переменной или изменяли его с помощью функций того модуля которому "принадлежит" эта переменная. Тогда и проблемы с переносом модуля в другие программы отпадут сами собой.


Цитата
Единственным "противопоказанием" может быть использование этой переменной в функции обработчике прерывания, в таком случае глобальной переменной нужно еще и volatile добавить при ее обявлении в с. файле модуля


Собственно с учетом всего этого и делаю. Уже давно, тема-то старенькая... Но вот чё-то заглянул.
ar__systems
Цитата(alekseykoj @ Dec 14 2009, 06:58) *
Извиняюсь blush.gif А так?


Извращение полное. Использовать динамическую память там где она апсалютно не нужна.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.