Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вопросы по изучению Си
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
A. Fig Lee
Цитата(haker_fox @ Mar 11 2009, 05:32) *
Еще раз повторюсь: даже если небольшой оверхед и есть, это мне не помешало поместить в ATmega16 ОСь + прикладную программу. Все на Си++. Сейчас занимаюсь реалтаймовским приложением. Тоже на Си++, без ОСи (она там просто не нужна). Для ATmega168. Я в свое время также и про Си считал. Только на ассемблере работал. Пока не попробывал. Удобств было... словами не описать.


Верю. И в Запорожец можно человек 7 натолкать. Вопрос - еффективно ли ето?
Скажите пожалуйста, что вас побудило использовать C++ a не C, и какие его
особеености Вы использовали(
try/catch, templates, STL, (design patterns maybe), virtual functions, operator overloading, .. etc)

Интересно будет послушать.


Цитата(vik0 @ Mar 11 2009, 14:12) *
У Страуструпа есть весьма интересная книжка - "Дизайн и эволюция языка С++". Не читали? Рекомендую. Одна из ключевых идей книги - "вы не платите за то, что не используете".
Не используете виртуальные функции - не будет никакой таблицы. Не инициализируете члены класса - не будет никаких лишних конструкторов (default ctor, инициализирующий члены default значениями, нормальный компилятор успешно соптимизирует).


Нет не читал. Не читаю на русском техническую литературу. подозреваю, что ето "The Annotated C++ Reference Manual"..
Лежит на работе. Почитываю иногда главки. все вместе нет смысла читать. Надо будет домой забрать, пока не украли.

А че там?

По сути - если мы все выбросим - ето будет C, только написанный в формате C++.

Собственно тут 2 вопроса - почему хорош C++ и почему он плох с мелкими микроконтроллерами..
1. С появлением C++ стало легче разрабатывать большие проекты - можно все разбить на обьекты,
менять их, легко видна структура программы, и т.д.

2. Почему он плох для мелкоконтроллеров:
он скрывает связь с ресурсами - например внутри конструктора может быть другой new,
что сразу не очевидно. Операторы могут быть переопределены, и так далее..
Не очень прозрачен. Деструкторы - надо помнить что при выходе из скопа вызывается деструктор и т.д.

Не, я по жизни и на работе в основном на C++ программирую,
но когда дома на мелкоконтроллерах - лучше С
haker_fox
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Верю. И в Запорожец можно человек 7 натолкать. Вопрос - еффективно ли ето?

В запорожце сколько сидячих мест? 4? Ну так зачем же туда 7 пихать?
В ATmega16 сколько памяти программ? 16Кб? Ну так если мое приложение + ОСь (на Си++) занимают 8 Кб памяти программ и 500 байт ОЗУ (используются буфера, много переменных), то кто мешает мне программировать на Си++? Я же не пытаюсь запихать туда более 16 Кб.
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Скажите пожалуйста, что вас побудило использовать C++ a не C, и какие его
особеености Вы использовали(
try/catch, templates, STL, (design patterns maybe), virtual functions, operator overloading, .. etc)
Интересно будет послушать.

Использовать Си++ меня побудила ОС scmRTOS. Увидев, что он успешно применяется для микроконтроллеров подобного класса, я решил что буду его применять в своей работе.
Какие особенности использовал? Так я же ссылку давал, там все есть.
Ну вот, хотябы это:
http://electronix.ru/forum/index.php?s=&am...st&p=548053
http://electronix.ru/forum/index.php?s=&am...st&p=548100
http://electronix.ru/forum/index.php?s=&am...st&p=548240
В последнем проекте активно использую наследование. Т.е. когда один класс наследуется другим. Очень удобно.
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
2. Почему он плох для мелкоконтроллеров:
он скрывает связь с ресурсами - например внутри конструктора может быть другой new,
что сразу не очевидно.

Чем это плохо и почему только для МК? На обычном IBM PC оперативы немеренно и бездумное использование new приветствуется, подумаешь пропадет пара сотен МБ? rolleyes.gif
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Операторы могут быть переопределены, и так далее..

Опять же, чем это плохо именно для МК?
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Не очень прозрачен.

Ну вот crying.gif
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Деструкторы - надо помнить что при выходе из скопа вызывается деструктор и т.д.

Ага, а еще надо помнить, что при создании объекта вызваются конструкторы и при вызове обработчика прерывания нужно сохранять SREG rolleyes.gif Конструктор и деструктор можно не определять в классе, тогда ничего вызваться не будет. Деструктор, как правило вообще редко применяется (ИМХО). А вот конструктор - это сплошная выгода. Создаешь объект и не заботишься о вызове функции на подобии Init(), конструктор все сам сделает. Чем плохо? Или вручную, или автоматом. Ну елки-палки, это же неубедительно звучит. Чем это снова плохо именно для МК?
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
Не, я по жизни и на работе в основном на C++ программирую,

Для ПК?
Цитата(A. Fig Lee @ Mar 12 2009, 09:39) *
но когда дома на мелкоконтроллерах - лучше С

А почему не АСМ, ведь на нем более оптимально можно написать? Си не очень прозрачен, добавляет свои навороты, оптимизацию, решает за программиста, где и что разместить biggrin.gif АСМ в этом плане гораздо лучше! В общем это юмор. А если серьезно, Вы попробуйте Си++ на МК. Глядишь, и сомнения пропадут, зато получите мощный инструмент на 8 битной платформе.

Ну и на последок: конечно, голову никто не отменял. Укладывать все возможности Си++ на AVR никто не собирается, но некоторые возможности почему бы не использовать?
vik0
Цитата(A. Fig Lee @ Mar 12 2009, 03:39) *
Нет не читал. Не читаю на русском техническую литературу. подозреваю, что ето "The Annotated C++ Reference Manual"..

Нет, я говорю о The Design and Evolution of C++
Цитата
А че там?

История развития языка с подробным описанием того, почему основные функции реализованы именно так, а не иначе.
Цитата
он скрывает связь с ресурсами - например внутри конструктора может быть другой new
что сразу не очевидно.

Ага. А на чистом "С" внутри функции типа InitSomething() не может быть вызвана InitSomethingElse() и, даже (о ужас!!), malloc??...
Цитата
Операторы могут быть переопределены...

Ну и отлично! Лично я предпочту написать:
Код
complex a, b, c, d;
....
a += b - c*d;

а не:
Код
a = complex_add(a, complex_sub(b, complex_mul(c, d)));

...и получить на выходе тот же самый код.
Цитата
Деструкторы - надо помнить что при выходе из скопа вызывается деструктор и т.д.

Ну и отлично! Лично я предпочту написать:
Код
int DoSomething()
{
  CriticalSection cs;
  ...bla-bla-bla...
  if (foo)
    return 1;
  ...bla-bla-bla...
  if (bar)
    return 2;
  ...bla-bla-bla...
  if (baz)
    return 3;
  ...bla-bla-bla...
  return 0;
}

а не:
Код
int DoSomething()
{
  DisableInterrupts();
  ...bla-bla-bla...
  if (foo) {
    RestoreInterrupts();
    return 1;
  }
  ...bla-bla-bla...
  if (bar) {
    RestoreInterrupts();
    return 2;
  }
  ...bla-bla-bla...
  if (baz) {
    RestoreInterrupts();
    return 3;
  }
  ...bla-bla-bla...
  RestoreInterrupts();
  return 0;
}

...и получить на выходе (извините за самоцитирование laughing.gif ) тот же самый код.



Цитата(haker_fox @ Mar 12 2009, 17:47) *
Ну и на последок: конечно, голову никто не отменял. Укладывать все возможности Си++ на AVR никто не собирается, но некоторые возможности почему бы не использовать?

+100
777777
Цитата(A. Fig Lee @ Mar 12 2009, 04:39) *
Деструкторы - надо помнить что при выходе из скопа вызывается деструктор и т.д.

Как раз наоборот. Когда ты пишешь на Си, то надо помнить, что всю память, которую ты выделяешь, после использования надо освободить. А в С++ (если, конечно, классы спроектированы правильно) память выделяется в конструкторе, а в деструкторе автматически освобождается. Так же с любой другой инициализацией/освбождением. Главное - правильно споектировать класс чтобы он точно отражал суть описываемого объекта.

Так что никакого оверхеда в С++ нет - все что он делает, нужно делать и в Си, но там это должен далеть сам программист вручную и ни о чем не забыть, а в С++ это сделается автоматически.
Rst7
Цитата(vik0 @ Mar 12 2009, 21:53) *
Ну и отлично! Лично я предпочту написать:
Код
...

а не:
Код
...

...и получить на выходе (извините за самоцитирование laughing.gif ) тот же самый код.


Вы просто не умеете готовить plain C:

Код
int DoSomething()
{
  UREG result=0;
  ATOMIC_BLOCK(_нужный_режим_)
  {
    ...bla-bla-bla...
    if (foo) {
                  result=1;continue;
    }
    ...bla-bla-bla...
    if (bar) {
                  result=2;continue;
    }
    ...bla-bla-bla...
    if (baz) {
                  result=3;continue;
    }
  }
  return result;
}


Как видите, все намного проще smile.gif

ЗЫ: ATOMIC_BLOCK можно покурить в инклудах гнуся. Кстати, в гнусе можно и прямо return 1 делать в ветках. Но я не сторонник пользовать гнутые фичи.
Сергей Борщ
Цитата(Rst7 @ Mar 13 2009, 09:16) *
Вы просто не умеете готовить plain C:
plain или непереносимые расшинения гнуся?
Rst7
Цитата
или непереносимые расшинения гнуся?


Где Вы увидели в моем примере непереносимые расширения? wink.gif Я специально обратил внимание, что в гнусе еще проще, но непереносимо.
vik0
Цитата(Rst7 @ Mar 13 2009, 09:16) *
Вы просто не умеете готовить plain C:

Умею. Но просто мне больше по вкусу С++ smile.gif
Цитата
Код
...

Как видите, все намного проще smile.gif

Согласен. Я и не утверждал что это нельзя сделать на С. Просто (лично мне) С++ вариант кажется более изящным, что ли. А оверхеда (о чем, собственно, изначально и шла речь) он не вносит.
Rst7
Цитата
непереносимые расшинения гнуся?


Дабы закрыть вопрос про переносимость. Вот такое я нынче в EWAVR пользую:

Код
#pragma inline=forced
UREG __get_state_and_cli(void)
{
  UREG v=__save_interrupt();
  __disable_interrupt();
  return v;
}

#define ATOMIC_BLOCK() for(UREG __iter=0,__state=__get_state_and_cli();!__iter;__restore_interrupt(__state),__iter++)


void TestAtomic(void)
{
  ATOMIC_BLOCK()
  {
    OSCCAL++;
  }
}


Цитата(vik0 @ Mar 13 2009, 09:41) *
Умею.


Вы выбрали плохой пример для демонстрации превосходства плюсов в количестве писанины. Что заставляет усомниться в умении smile.gif
_Pasha
Цитата(Rst7 @ Mar 13 2009, 11:53) *
Дабы закрыть вопрос про переносимость. Вот такое я нынче в EWAVR пользую:

Кстати, а в ИАРе return внутри for() как себя чувствует?
Rst7
Цитата
Кстати, а в ИАРе return внутри for() как себя чувствует?


В смысле внутри {}? Как обычный return. Т.е. тут он не восстановит состояние SREG. В гнусе хитрый костыль для этого есть.
MrYuran
Цитата(_Pasha @ Mar 13 2009, 11:15) *
Кстати, а в ИАРе return внутри for() как себя чувствует?

А что с ним может стать?
Но вообще это дурной стиль.
лучше break;
Rst7
Цитата
лучше break;


Конкретно в такой критической секции, изготовленной из for, нужен continue
_Pasha
Цитата(Rst7 @ Mar 13 2009, 12:19) *
тут он не восстановит состояние SREG

Ну, это понятно. При всей красоте ATOMIC_BLOCK, всегда надо помнить, из чего он сделан smile.gif

Я даже инициализацию требухи в начале main() пишу не иначе как ATOMIC_BLOCK(ATOMIC_FORCEON), с явным нежеланием отказываться от этой фичи в дальнейшем.
Rst7
Цитата
всегда надо помнить, из чего он сделан


В гнусе есть специальный костыль - суть плюсовый деструктор переменной. Вот он и пользуется в гнусевом атомике. Можно из такого блока выходить чем угодно, хоть return, хоть break, хоть goto (не уверен). longjump - нельзя.
_Pasha
Цитата(Rst7 @ Mar 13 2009, 12:33) *
Можно из такого блока выходить чем угодно... хоть goto (не уверен)


Проверил - можно. smile.gif
Текст для проверки:
Код
volatile int ff;

int main (void) __attribute__((OS_main));

int main (void)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
DDRB = 1;
if(ff) goto aaa;
PORTB=1;
}
ff += 41*DDRB;
aaa:
for(uint8_t j=0; j<3;j++)
{
ff += pgm_read_byte(&(BCD[j]));
}


return 0;
}

Листинг
CODE

00000070 <main>:

int main (void) __attribute__((OS_main));

int main (void)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
70: 2f b7 in r18, 0x3f ; 63
return 1;
}

static __inline__ uint8_t __iCliRetVal(void)
{
cli();
72: f8 94 cli
{
DDRB = 1;
74: 31 e0 ldi r19, 0x01 ; 1
76: 34 b9 out 0x04, r19 ; 4
if(ff) goto aaa;
78: 80 91 08 01 lds r24, 0x0108
7c: 90 91 09 01 lds r25, 0x0109
80: 89 2b or r24, r25
82: 09 f0 breq .+2 ; 0x86 <main+0x16>
84: 39 c0 rjmp .+114 ; 0xf8 <main+0x88>
PORTB=1;
86: 35 b9 out 0x05, r19 ; 5
(void)__s;
}

static __inline__ void __iRestore(const uint8_t *__s)
{
SREG = *__s;
88: 2f bf out 0x3f, r18 ; 63
}
ff += 41*DDRB;
8a: 80 91 08 01 lds r24, 0x0108
8e: 90 91 09 01 lds r25, 0x0109
92: 24 b1 in r18, 0x04 ; 4
94: 49 e2 ldi r20, 0x29 ; 41
96: 24 9f mul r18, r20
98: 90 01 movw r18, r0
9a: 11 24 eor r1, r1
9c: 28 0f add r18, r24
9e: 39 1f adc r19, r25
a0: 30 93 09 01 sts 0x0109, r19
a4: 20 93 08 01 sts 0x0108, r18
aaa:
for(uint8_t j=0; j<3;j++)
{
ff += pgm_read_byte(&(BCD[j]));
a8: 80 91 08 01 lds r24, 0x0108
ac: 90 91 09 01 lds r25, 0x0109
b0: e4 e3 ldi r30, 0x34 ; 52
b2: f0 e0 ldi r31, 0x00 ; 0
b4: 24 91 lpm r18, Z+
b6: 82 0f add r24, r18
b8: 91 1d adc r25, r1
ba: 90 93 09 01 sts 0x0109, r25
be: 80 93 08 01 sts 0x0108, r24
c2: 80 91 08 01 lds r24, 0x0108
c6: 90 91 09 01 lds r25, 0x0109
ca: 31 96 adiw r30, 0x01 ; 1
cc: 24 91 lpm r18, Z+
ce: 82 0f add r24, r18
d0: 91 1d adc r25, r1
d2: 90 93 09 01 sts 0x0109, r25
d6: 80 93 08 01 sts 0x0108, r24
da: 80 91 08 01 lds r24, 0x0108
de: 90 91 09 01 lds r25, 0x0109
e2: 31 96 adiw r30, 0x01 ; 1
e4: e4 91 lpm r30, Z+
e6: 8e 0f add r24, r30
e8: 91 1d adc r25, r1
ea: 90 93 09 01 sts 0x0109, r25
ee: 80 93 08 01 sts 0x0108, r24
}


return 0;
}
f2: 80 e0 ldi r24, 0x00 ; 0
f4: 90 e0 ldi r25, 0x00 ; 0
f6: 08 95 ret
f8: 2f bf out 0x3f, r18 ; 63
__asm__ volatile ("" ::: "memory");
fa: d6 cf rjmp .-84 ; 0xa8 <main+0x38>
Kirill24
Посоветуйте книжку для НАЧИНАЮЩЕГО(чайника)smile3046.gif по Си-программированию для микроконтроллеров. Можно на английском или немецком. Самое главное, чтобы матерьял объснялся хорошо.

Зарание всех благодарю за помощь santa2.gif
chief_olimp
Шпак Ю.А.
Программирование на языке С для AVR и PIC микроконтроллеров.
МК-Пресс, Киев, 2006, 400стр. ил.
Часть 1
Часть 2
Ссылка отсюда
Неполохой help по avr-libc
Это все для среды WINAVR если речь идет про AVR. Вообще WINAVR посложнее будет чем к примеру CV, но в нем меньше закрытости кода что ли, за счет меньшего количества готовых функций. Отсюда большая гибкость но это уже совсем другой вопрос. Удачи в освоении
kurtis
Цитата
Си-программированию для микроконтроллеров

Программирование на С для МК ничем принципиально не отличается от программирования для больших ПК. Мне очень понравился Герберт Шилдт "Полный справочник по С".

Цитата
Можно на английском или немецком.

Книги ищите в торрентах. Например поищите здесь по запросу embedded.
smac
Цитата(Kirill24 @ Oct 25 2009, 14:57) *
Посоветуйте ...

Как начинающий посоветую Кернигана и Ричи - учиться можно на ПК, а параллельно программить для МК, читая доки к выбранному компилятору.
arm123
У меня такой вопрос:
к примеру есть функция с такой конструкцией:

Код
какие-то действия
if( условие )
{
   ***
}
else
{
  ***
}


В одной ветке мне нужна для работы локальная переменная, к примеру count, а для другого не нужно. Как лучше обьявлять эту переменную?
Вот так:
Код
какие-то действия
if( условие )
{
   uint32_t count;
   ***
}
else
{
  ***
}


млм же предпочительнее все-таки так:
Код
uint32_t count;
какие-то действия
if( условие )
{
   *** (тут count используется)
}
else
{
  *** (тут count не используется)
}


Спасибо.
sergeeff
Г.Саттер, А.Александреску "Стандарты программирования на С++. 101 правило и рекомендации" :

правило 18 - Объявляйте переменные как можно локальнее. Переменных должно быть как можно меньше, а время их жизни - как можно короче.

Подробно в книге на стр. 47
WHALE
Дык в топике вроде речь идет о С? И если компилятор не поддерживает С99,то тогда без вариантов..
arm123
Я пользуюсь компилятором IAR. Сдесь вроде поддерживает smile.gif раз уж локально можно обьявлять где угодно
mdmitry
И не забыть про реентабельность(переносимость) кода.
ReAl
Цитата(WHALE @ Nov 9 2009, 15:02) *
Дык в топике вроде речь идет о С? И если компилятор не поддерживает С99,то тогда без вариантов..
В начале блока { } можно объявлять хоть в до-ансишном K&R, у которого ещё прототипов функций не было, так что в данном случае именно
Код
if( условие )
{
   uint32_t count;
   ***
}
else
{
  ***
}
или, к примеру, так
Код
switch( aaa )
{
    case bbb:
    {
        uint32_t count;
        ***
    }
    break;
    case ccc:
    {
        float sum;
        ***
    }
    break;
}
и это должен нормально отработать любой компилятор.
arm123
С точки зрения быстродействия все таки да мне кажется переменные нужно объявлять как можно локальнее... к примеру тогда максимальное число переменных будут загружаться через регистры а не через память. Прав я или нет? В чем ещё преимущества объявления переменных как можно локальнее.
sergeeff
Цитата(arm123 @ Nov 9 2009, 17:37) *
С точки зрения быстродействия все таки да мне кажется переменные нужно объявлять как можно локальнее... к примеру тогда максимальное число переменных будут загружаться через регистры а не через память. Прав я или нет? В чем ещё преимущества объявления переменных как можно локальнее.


Я привел ссылку на конкретную книгу где все по пунктам расписано. Наверное не сложно эту книгу найти и прочесть.
arm123
Цитата(sergeeff @ Nov 9 2009, 18:30) *
Я привел ссылку на конкретную книгу где все по пунктам расписано. Наверное не сложно эту книгу найти и прочесть.

Там анализ идет в первую очередь касательно удобочитаемости и большей вероятностью ошибится нежели вопрос оптимизации и быстродействия.
kurtis
Не забудьте инициализировать переменную, т.е. должно быть как-то так
Код
какие-то действия
if (условие) {
   uint32_t count = SOME_VALUE; // какое-то нужно Вам значение
   ***
}
else {
  ***
}

У Вас тут ошибки не закралось? Я хочу сказать, что за переделами if(){} не будет существовать такой переменной как count или будет использована переменная из глобальной области видимости.
Как дальше по тексту будет использоваться переменная count?
arm123
Цитата(kurtis @ Nov 9 2009, 20:05) *
У Вас тут ошибки не закралось? Я хочу сказать, что за переделами if(){} не будет существовать такой переменной как count или будет использована переменная из глобальной области видимости.
Как дальше по тексту будет использоваться переменная count?

ну если быть точным то ситуация такая
Код
uint32_t count;
какие-то действия
if( условие )
{
   *** (тут count используется)
}
else
{
  *** (тут count не используется)
}
***
(тут count не используется)


Ну я для себя ответ понял, переменные нужно делать максимально локальнее, за исключением циклов smile.gif
Waso
Добрый день! или не день =) ... Влеплю сюда вопрос по Си++.

Не пойму как реализовать следующую идею:
Заводим класс клавиатуры, в котором описан метод(процедура) опроса клавиатуры, которая обновляет находящуюся в этом-же классе переменную - битовую маску нажатых клавиш. (сколько клавиш-столько бит)
Код
class MtxKeypad
{
private:
  volatile KeyMapType NewKeyMap;   //  считываемая в данный момент карта клавиш
public:
  volatile KeyMapType KeyMap;       // последняя считанная карта клавиш

  // typedef Mtx_Button<1,1> Key_1;        // !! эти строки относятся к вопросу сабжа
// typedef Mtx_Button<1,2> Key_2;        // тут объявляются все кнопки и их расположение (строка,столбец)
// typedef Mtx_Button<2,1> Key_Enter;    
// typedef Mtx_Button<4,1> Key_Cancel;

  void Scan() {  // метод опроса клавиатуры
// ....
      KeyMap=NewKeyMap;
// ....
  }
};

далее заводим шаблон класса отдельной кнопки, который умеет сообщать, нажата эта кнопка или нет:
Код
template <int Col, int Row>
class Mtx_Button : public MtxKeypad
{
    //friend class MtxKeypad;    // это вот ХЗ надо тут или нет
private:
static const KeyMapType mask=(1UL<<(Row*5+Col));
public:
  inline uint8_t Pressed() {return (MtxKeypad::KeyMap & mask)!=0;}
};

И нужно использовать потом так:
Код
int main()
{
    MtxKeypad Keys;
    Mtx_Button<4,1> Key_A;
    for(;;)
    {
        Keys.Scan();
        if(MtxKeypad::Key_1::Pressed()) pin2.Toggle();  // ошибка
        if(Key_A.Pressed()) pin2.Toggle();  // работает если добавить Key_A.Scan(); но тут отдельный объект с отдельными переменными - неправильно
    }
}

Так вот ВНИМАНИЕ ВОПРОС: как правильно описать классы, если мне надо чтоб дочерние объекты не копировали родительский метод и переменные, а пользовались ими, влияли на них.
AHTOXA
Наследовать кнопку от клавиатуры - это, имхо, переборsmile.gif Достаточно хранить в кнопке ссылку на клавиатуру.
Типа так:
Код
// шаблон кнопки
template<int row, int col, typename matrix>
class button
{
private:
    matrix& m_;  // ссылка на клавиатуру
public:
    button(matrix& M): m_(M) {}
    bool pressed() { return m_.keymap & (row*5+col); }
};

// класс клавиатуры
class kbd
{
public:
    button<0, 0, kbd> Enter(this);
    void Scan();
}

void main()
{
    kbd Kbd;
    Kbd.Scan();
    if (Kbd.Enter.presed())
    {
    }
}
Сергей Борщ
Цитата(Waso @ Nov 13 2009, 17:13) *
чтоб дочерние объекты не копировали родительский метод и переменные, а пользовались ими, влияли на них.
Делать такие функции-члены и переменные родителя статическими. Если родитель может существовать более чем в одном экземпляре - надо вместо наследования использовать указатель или ссылку. Т.е. кнопка живет отдельным объектом и в конструкторе получает ссылку или указатель на клавиатуру. Или кнопка является членом класса клавиатуры, и для доступа к клавиатуре получает ссылку или указатель в конструкторе.
Waso
Хорошо, но тогда эти ссылки будут храниться в ОЗУ, а ето непозволительный оверхед! Если даже в классе кнопки ссылку обьявить как константу и задавать в конструкторе, компилятор всеравно ее пихает в ОЗУ.. sad.gif
Сергей Борщ
Цитата(Waso @ Nov 15 2009, 12:34) *
Если даже в классе кнопки ссылку обьявить как константу и задавать в конструкторе, компилятор всеравно ее пихает в ОЗУ.. sad.gif
А что ему еще делать? Если клавиатура одна - делайте ее члены и функции-члены статическими в базовом классе. Если клавиатур у вас несколько - кнопка должна знать, к какой имеено обращаться. Значит, ей нужна переменная для хранения указателя или ссылки. Даже если этот указатель компилятор расположит во флеше (и таких указателей несколько, ведь клавиатур тоже несколько) - ему надо как-то эти указатели различать, значит он вынужден будет хранить в кнопке какие-то данные, позволяющие выбрать нужный указатель.
kurtis
Встретил в исходниках такую вот конструкцию
Код
#define putnstr(str,n)  do {            \
        printf ("%.*s", n, str);    \
    } while (0)

Почему использован цикл do-while ведь все-равно printf() один раз будет вызвано, как с ним, так и без него?
XVR
Цитата(kurtis @ Nov 18 2009, 01:33) *
Встретил в исходниках такую вот конструкцию
Код
#define putnstr(str,n)  do {            \
         printf ("%.*s", n, str);    \
     } while (0)

Почему использован цикл do-while ведь все-равно printf() один раз будет вызвано, как с ним, так и без него?
Такая конструкция применяется если в теле макроса более одной операции (в данном случае эта конструкция излишняя). Пример:
Код
#define func(a) do { aa(a); bb(a); } while(0)
#define func2(a) { aa(a); bb(a); }

if (some) func(a); else blablabla(a); // Ok
if (some) func2(a); else blablabla(a); // Syntax error
mdmitry
Код
do { aa(a); bb(a); } while(0)

Уже обсуждалось, поиск в помощь, сообщение от Сергея Борща было.
Herz
Цитата
if (some) func2(a); else blablabla(a); // Syntax error

Разве? Почему?
defunct
Цитата(Herz @ Nov 18 2009, 17:39) *
Разве? Почему?


Код
if (1)
{
};
else
{
}

потому что - Syntax error near 'else'. ";" после закрывющей скобки это уже второй оператор (пустой оператор), а else получается как сам по себе.
верхнее равносильно такому:
Код
if(1)
{
   foo1();
}
foo2();
else
{
}
rezident
Цитата(Herz @ Nov 18 2009, 20:39) *
Разве? Почему?
Потому, что между if и else знак ";" (точка с запятой) встречается.
Herz
Цитата(rezident @ Nov 18 2009, 17:59) *
Потому, что между if и else знак ";" (точка с запятой) встречается.


Хм, ну так не ставить его и всё. laughing.gif Оно, может, на читабельность как-то повлияет... По-моему, даже положительно. Понятно станет, что макрос.
XVR
Цитата(Herz @ Nov 18 2009, 23:26) *
Хм, ну так не ставить его и всё. laughing.gif Оно, может, на читабельность как-то повлияет... По-моему, даже положительно. Понятно станет, что макрос.
Понятно станет, если БУДЕТ известно, что макрос. А если нет, то понятно станет, что ';' забыли поставить crying.gif
Herz
Цитата(XVR @ Nov 19 2009, 09:52) *
Понятно станет, если БУДЕТ известно, что макрос. А если нет, то понятно станет, что ';' забыли поставить crying.gif


Если забыли, компилятор такого точно не пропустит.
Сергей Борщ
Цитата(Herz @ Nov 19 2009, 10:18) *
Если забыли, компилятор такого точно не пропустит.
Потом по каким-то причинам вы захотите заменить макрос на честную функцию и будете перелопачивать всю программу, исправляя непонятно откуда появившиеся ошибки? Не проще ли сразу написать в макросе do {} while(0) ?
baralgin
Цитата(Сергей Борщ @ Nov 19 2009, 11:38) *
Потом по каким-то причинам вы захотите заменить макрос на честную функцию и будете перелопачивать всю программу...

А почему сразу функции не писать? В смысле бывают ситуации когда без них не обойтись? ("инлайнизация" не в счёт, т.к. обычно легко решается этот вопрос).
defunct
Цитата(baralgin @ Nov 19 2009, 22:47) *
В смысле бывают ситуации когда без них не обойтись?

Даже когда включена максимальная оптимизация и inline функция действительно инлайнится, все равно на практике встречаются ситуации когда макросы дают более эффективный код.

Бывают еще такие ситуации:

file1.h:
Код
#include "file2.h"

typedef struct
{
   ...
   ..
   .
   some_file2_enum_type field_n;
   ..
} some_complex_struct_t;

А в file2.h ну очень хочется объявить функцию которая работает с field_n из file1.h, а подключить file1 низя. Макрос спасает.


Или вот конкретная ситуация, хотим чтобы string литерал не занимал RAM впустую.
baralgin
Цитата(defunct @ Nov 20 2009, 01:53) *
Даже когда включена максимальная оптимизация и inline функция действительно инлайнится, все равно на практике встречаются ситуации когда макросы дают более эффективный код.

Только что столкнулся как раз с таким. Keil (-O3, Otime ) отказался инлайнить очевидную функцию(static), даже __INLINE в этом случае не воспринял crying.gif , хотя в других местах за ним такого не замечал(постоянно слежу за получаемым кодом). Видимо решил сэкономить, т.к. подряд 10 "вызовов". В итоге "макросный" вариант выполняется в два раза быстрее...

В предложенном первом примере, нельзя разве в функцию передать указатель(или ссылку) на поле? Хотя как поведет себя компилятор в таком случае конечно же вопрос, да и не очень эстетично выходит в целом. Примерно понятно в общем.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.