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

Вопрос к знатокам С.

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

 

на счет "специально заведенных переменных"... на сколько я понимаю, при определенном напряжении сил можно очень сложные выражения (например, нечто типа БПФ) уместить в единственном операторе, избежав "лишних" переменных (обычно они называются локальными)... и, хотя с точки зрения результата это будет совершенно верное "выражение", назвать его удобочитаемым будет можно вряд ли. скорее всего и по объему результирующего кода результат не будет отличаться от "развернутого" варианта...

 

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

 

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

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


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

Если Вам приятно в таком духе - продолжайте, утрируйте, доходите до нелепиц и спорьте с ними - но без меня.

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

Если бы эта запись была необходимым или достаточным условием для чтения из переменной в правой части независимо от её volatile-ности, то в этом был бы смысл.

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

for( i = 0; i < 5; ++i /* инкрементируем счётчик цикла */)
{
}

(хотя для малознакомого с С новичка этот комментарий и повышает читаемость), так как она всего лишь косвенно, через запсиь в другую переменную, говорит "а мы эту переменную прочитали!" для того, кто этого сам не видит. Всего лишь.

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


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

:a14:

Обсуждение напоминает обсуждение кода типа

unsigned char a
if (a == a) {a=a;}

"Индийский код..."

Чем не устраивает вариант с просмотром дизассемблерного варианта полученного кода? Все сразу встанет на свои места - что лучше, а что хуже..

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


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

хочу для универсализма сделать версию функции printf() для вывода строки на 7-сегментный индикатор, при этом реализация получается примерно такая:

void printf_7led(char *format,  ... ){
   char st[20];
   sprintf(&st, format, ???); // вот тут трабла - обозначена вопросами
   // далее мой код, обрабатывающий st[]
}

трабла такая: как передать "хвост" аргументов, полученных на входе в printf_7led непосредственно в вызываемый sprintf?

P.S. Работаю с WinAVR (GCC)

Изменено пользователем ARV

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


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

void printf_7led(char *format,  ... ){
    va_list args;
    va_start(args, format);
    char st[20];
    vsprintfs(st, format, args);
    va_end(args);

Но это не совсем правильный с точки зрения avr_libc метод. правильно будет примерно так:

int putchar_7led(int symbol, FILE *stream)
{
...
}
FILE Display;

void main()
{
    Display =  FDEV_SETUP_STREAM(putchar_7led, NULL, _FDEV_SETUP_WRITE);


   fprintf(&Display, "%s", "Hello");
   for(;;);
}

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


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

большое спасибо! я вокруг чего-то похожего бродил, но до верного решения так и не дотумкал.

а на счет вывода в поток "дисплея" - мне кажется, это хоть и красивый метод, но для 6-символьного 7-сегментного дисплея наверное слишком избыточный... просто строчку вывел - сразу понятно, что предыдущая затерлась... а при посимвольном выводе надо как-то так же красиво и логично отслеживать момент, когда строка кончилась... не находите?

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


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

мне кажется, это хоть и красивый метод, но для 6-символьного 7-сегментного дисплея наверное слишком избыточный... просто строчку вывел - сразу понятно, что предыдущая затерлась... а при посимвольном выводе надо как-то так же красиво и логично отслеживать момент, когда строка кончилась... не находите?
Ну избыточный он не этим - на шести символах просто толком негде разгуляться форматными строками с ширинами полей и т.п.

А что касается "отслеживать момент" - да на раз, було бы желание покуражиться.

Можно заставить по '\n' очищать строку - причём:

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

"\nHELLO", что будет приводить к очистке и выводу, последующий вывод допишет в конец

- либо по нему запоминать, что строка была завершена и уже последующий вывод очистит и начнёт с начала строки, тогда выводить (&Display, "Err %02X\n", err_code);

 

Во втором случае в putchar_7led заводится статический флаг, который запоминает прохождение '\n'

int putchar_7led(int symbol, FILE *stream)
{
    static bool newline = true;
    if( symbol == '\n') {
        newline = true;
    } else {
        if( newline ) {
            // прочистить пробелами весь индикатор и поставить указатель символа
            // на начало буфера динамической индикации
        }
        newline = false;
         // вывести символ в текущую позицию
    }
}

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


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

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

А что касается "отслеживать момент" - да на раз, було бы желание покуражиться.

как раз желания куражиться и не было.

я поступил по первому совету - сделал оберточные функции для sprintf с преобразованием нормальных символов в семисегментные и весьма доволен результатом. на верхнем уровне и для семисегментника пишу printf_7led("%.2u", var) - и все выводится :) а когда надо, то и так работает printf_7led("stop"). жаль, не все буквы в семисегментные варианты преобразуются :) особенно кириллица - слишкам многа букафф :)

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


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

...сделал оберточные функции для sprintf

http://electronix.ru/forum/index.php?showtopic=56439&hl=

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


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

ну да, это и имел ввиду - vsprintf конечно

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


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

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

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

 

Во-первых избегать появления дурного кода - т.е. сводить в минимуму потребность в таких вот "var;" и лишних переменных. С какого бодуна драйвер решил вдруг опустошить содержимое буферов? Ведь можно построить драйвер так, что тупое опустошение не понадобится вовсе! Или почему SPSR надо обязательно читать в никуда?

Можно же хотя бы в целях статистики хранить последнее значение SPSR:

ISR()
{
   ...
   spiContext.LastSpsrVal = SPSR;
}

производительности-то оно не сожрет.

 

во-вторых - применять практику самодокументируемого кода - завести однозначно понятные макросы:

#define access( x )  // обращение к X
#define ignore( x )  // игнорировать x
...

 

Встретив в коде

access( SPSR );

Даже те "кто в танке" сразу поймут, что делается с SPSR.

А столкнувшись с "финтом" компилятора - достаточно поменять только тело макроса...

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


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

С какого бодуна драйвер решил вдруг опустошить содержимое буферов? Ведь можно построить драйвер так, что тупое опустошение не понадобится вовсе!

Научите :( у LPC2000 за тем-же SPI есть FIFO которое

- не отключается

- нет возможности сбросить его

Как сделать запись по SPI так, что-бы когда-нибудь при чтении можно было прочтать SPI, а не содержимое забитого при записи FIFO.

Или почему SPSR надо обязательно читать в никуда?

Можно же хотя бы в целях статистики хранить последнее значение SPSR:

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

Я бы не отказался и от обратной возможности - записи "ничего", а то бывает приходится писать, например,

VICVectAddr = i;            // Dummy write to signal end of interrupt

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

 

spiContext.LastSpsrVal = SPSR;

производительности-то оно не сожрет.

Да? Вместо, например, команды чтения в первый попавшийся расходный регистр будет производится считывание (для load/store через тот-же промежуточный регистр) в структуру находяшуюся в памяти.....

Даже те "кто в танке" сразу поймут, что делается с SPSR.

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

масло(масло);

Начинаешь думать, а чего это вдруг так написали - может смысл какой есть? Смотришь header спрашиваешь и тебе объясняют,что это масло для танкистов.... :). В общем-то конечно можно макрос написать, но лишняя сущность.

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


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

Я бы не отказался и от обратной возможности - записи "ничего", а то бывает приходится писать, например,

VICVectAddr = i;            // Dummy write to signal end of interrupt

А почему не так:

VICVectAddr = 0;

Чтобы избежать лишнего обращения к константе? По-идее будет взята из генератора констант...

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


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

VICVectAddr = 0;

Чтобы избежать лишнего обращения к константе? По-идее будет взята из генератора констант...

Полноразмерная Константа будет взята из памяти, или сгенерирована в регистре, но все это уже громоздко по сравнению с минималистичной записью первого попавшегося регистра c произвольным значением. По этой причине и указываю имя некой "недалеко" находящейся переменной.

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


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

Научите :( у LPC2000 за тем-же SPI есть FIFO которое

- не отключается

- нет возможности сбросить его

Как сделать запись по SPI так, что-бы когда-нибудь при чтении можно было прочтать SPI, а не содержимое забитого при записи FIFO.

Думаю, счетчик отправленных байт должен помочь. Драйвер будет вытаскивать из FIFO ровно то количество байт, которое он запихнул при записи.

 

Я бы не отказался и от обратной возможности - записи "ничего", а то бывает приходится писать, например,

VICVectAddr = i;            // Dummy write to signal end of interrupt

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

Можно дать константу (опять же если производительности хватает).

VICVectAddr = 0;

или

VICVectAddr = CurrentVector;

будет чуть-чуть дольше, зато интуитивно понятно, что делается.

 

Да? Вместо, например, команды чтения в первый попавшийся расходный регистр будет производится считывание (для load/store через тот-же промежуточный регистр) в структуру находяшуюся в памяти.....

Да, тут сознательно добавляем запись в память. Медленнее чем в регистр - да, сильно скажется на производительности всей системы - нет! (всего лишь запись одной ячейки на FIFO циклов SPI). Польза от этой записи - когда свалимся в DABT, можно будет снять дамп и посмотреть состояние железа, в т.ч. и значение SPSR (вдруг именно его значение поможет найти причину аборта).

 

Я говорю не отказаться, а свести к минимуму потребность в пустых чтениях, пустых переменных, и прочем дурном коде, и чтобы не быть пустословным показываю как можно этого добиться. А выбор делать так или нет - как всегда за разработчиком.

 

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

масло(масло);

:)

Ну всяко лучше так, чем взять банку с желтым содержимым без надписи и понять что это совсем не масло, когда содержимое уже плеснули на сковородку. :)

 

В общем-то конечно можно макрос написать, но лишняя сущность.

Дело привычки. Например как Вы поступите если есть некий шаблон колбека:

 

typedef void (*putmsg_cb)(char *, int, int)

 

и куча функций подпадающих под этот шаблон, но не использующих все параметры?

 

Мне удобно поступать так:

 

void xx_putmsg( char *msg, int size, int priority)
{
    ignore( priority ); // <-- избавились от warning'a и сразу видно, что параметр priority нафиг не нужен
    ...
}

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


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

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

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

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

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

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

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

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

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

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