Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Оператор GoTo
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
andron86
Цитата(ValBag @ Oct 24 2007, 08:19) *
Для примера в программе "кодовый замок" из книги Белова А.В., в небольшом тексте программы на CИ в модуле main 7 переходов. И все работает нормально. Может быть, кто то потвердит вводное утверждение и покажет как надо на приложенном тексте.


Работать goto будет всегда, если правильно запрограммировал! Просто суть в том, что человеку будет труднее понять вашу функцию с 7 ohmy.gif goto, хотя и спорно. Мне, например труднее понять с do while.

Вообщее это дело вкуса.
alexander55
Цитата(andron86 @ Oct 24 2007, 11:23) *
Работать goto будет всегда, если правильно запрограммировал!

Я бы не был таким оптимистом. М.б. при использовании другого компилятора, будет как раз неправильно с использованием goto. Или небольшое изменение с добавлением скобочек {}, подумайте и станет печально. Разве это хорошо ?
zltigo
Цитата(alexander55 @ Oct 24 2007, 10:58) *
М.б. при использовании другого компилятора....

Вот так и рождаются предрассудки sad.gif. Сказал "может быть", помянул всуе "компилятор" и все понеслось...
alexander55
Цитата(zltigo @ Oct 24 2007, 12:07) *
Вот так и рождаются предрассудки sad.gif. Сказал "может быть", помянул всуе "компилятор" и все понеслось...

Я замолкаю. Такие темы меня заводят. smile.gif
andron86
Цитата(alexander55 @ Oct 24 2007, 09:58) *
подумайте и станет печально.

Печального ничего не будет, если всё правильно сделали wink.gif
ValBag
Прошу прощения за создание новой темы без поиска.
Как заметил IEC и andron86 я начинал с ASM-а, поэтому c GoTo программа читается "влет", а труднее понять с do while и т.п.

DASM
Просьба еще раз просмотреть Ваш код и поправить неточности. Для меня это было бы хорошим наглядным пособием!
То, что мне непонятно:
1. Нет выхода из последнего вложенного цикла while(1).
2. Как мы попадаем в сегмент проверки кода?
3. В предпоследнем операторе while(1) первый оператор continue должен передать управление на следующую итерацию упомянутого while(1), но это неверно по сути того, что должна делать программа. Должен быть переход на первый оператор while(1).
Может быть я ошибаюсь. Просьба меня поправить.
Maddy
А собственно чего все так на бедного goto оплчились ? IMHO Даден инструмент в языке - надо - пользуйся , только думай что делаешьwink.gif Молотком тоже можно самого себя прибитьwink.gif но их-же никто не запрещает...
А ведь были еще setjmp/longjmp wink.gif про них вообще можно много интересного рассказать wink.gif и тож goto в каком-то смысле wink.gif
aesok
GCC Bugzilla:

Only very good and very bad programmers use goto in C
DASM
Не отношу себя ни к вери гуд ни вери бэд, но со временем согласен, все меньше следую умным книжкам и пользую то, что удобно.
ValBag - выходы там по break , но ошибки у меня есть точно, а голова после ночных посиделок не варит, попробуйте сами напрячься smile.gif
alexander55
Цитата(aesok @ Oct 24 2007, 14:04) *
Only very good and very bad programmers use goto in C

Наверное, в этом все дело. a14.gif
ReAl
Цитата(IEC @ Oct 24 2007, 09:07) *
А вообще-то стиль написания напомнил asm.
Там комментарий есть такой вот
This program was produced by the CodeWizardAVR V1.24.4 Standard Automatic Program Generator
Не знаю - что за генератор и из чего он генерировал, но на "трансляцию" блок-схемы с ромбиками проверки условий очень похоже.
А транслятору блок-схем в С как промежуточный язык можно и разрешить ставить goto в немеряных количествах - пока получившийся текст не предназначен для чтения человеком и пока "сопровождается" блок-схема, а не сгенерированный из неё С-код.

Честно говоря, программа и организована как-то странно, "первым проходом" я её переписал бы так:
CODE
#define F_CPU 4000000UL
#include <tiny2313.h>

#define SYSTICK_MS 25 // приблизительно 24 мс период прерываний
// прескалер для таймера 1 1/64
#define SYSTICK_DELTA ((F_CPU / 64 * SYSTICK_MS + 500) / 1000) // смещение OCR
// времена в милисекундах, максимум SYSTICK_MS * 255
#define KEY_TIMEOUT (1000 / SYSTICK_MS)
#define LOCK_DELAY (3000 / SYSTICK_MS)
#define KEY_MASK 0x77F // маска используемых клавиатурой битов
#define BSIZE 30 // Размер буфера для хранения кода

unsigned int bufr[BSIZE]; // Буфер в ОЗУ для хранения кода
#pragma warn-
eeprom unsigned char klen; // Ячейка для хранения длины кода
eeprom unsigned int bufe[BSIZE]; // Буфер в EEPROM для хранения кода
#pragma warn+

volatile unsigned char timeout;
volatile unsigned key_filtered = 0;
volatile unsigned char key_changed = 0;

// Прерывание по совпадению в канале A Таймера 1
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
static unsigned prev;
unsigned current;

OCR1A += SYSTICK_DELTA;
if( timeout) --timeout;

// мне больше нравится, когда нажатая кнопка даёт единичку, ничего не нажато - нули
// поэтому инвертируем состояние PIN
current = KEY_MASK & ~((PINB << 8) | PIND);
if( current == prev ) { // не дребезжит
if( key_filtered != current) { // и поменялось
key_changed = 1;
key_filtered = current;
}
}
prev = current;
}

unsigned int get_key(void)
{
unsigned key = 0;
if( key_changed) {
#asm ("cli");
key = key_filtered;
key_changed = 0;
#asm ("sei");
}
return key;
}

// Основная функция
void main(void)
{
unsigned char key_index; // Указатель массива
unsigned char i; // Вспомогательный указатель
unsigned int key;

PORTB = 0xE7; // Порт B
DDRB = 0x18;

PORTD = 0x7F; // Порт D
DDRD = 0x00;

TCCR1A = 0x00; // Таймер/Счетчик 1
TCCR1B = 0x03;
TCNT1 = 0;
OCR1A = SYSTICK_DELTA;

ACSR = 0x80; // Аналоговый компаратор
#asm ("sei");

while (1) { // Главный цикл
key_index = 0;
// Пока буфер пуст либо начали набирать, но не кончился таймаут - ждём нажатий
while( key_index == 0 || timeout != 0 ) {
key = get_key();
if( key ) {
bufr[key_index] = key;
timeout = KEY_TIMEOUT;
if( ++key_index >= BSIZE)
break;
}
}

// полный буфер или пауза между клавишами > KEY_TIMEOUT, реакция на набранный код
if (PINB.7 == 1) { // Проверка переключателя режимов
//------------------------------ Проверка кода
if ( key_index != klen ) // Проверка длины кода
continue;
for (i = 0; i < key_index; i++)
if (bufe[i] != bufr[i]) // Проверка самого кода
continue;
} else {
//------------------------------ Запись кода в EEPROM
klen = key_index; // Запись длины кода
for (i = 0; i < key_index; i++)
bufe[i] = bufr[i]; // Запись всех байтов кода
}

//------------------------------ Открывание замка
// оставлено как было - замок открывается и после ввода нового кода, возможно,
// это было задумано как подтверждение замены.
PORTB.4 = 1; // Открываем замок
timeout = LOCK_DELAY;
while( timeout) ;
PORTB .4 = 0; // Закрываем замок
}
}

Количество строк С-программы ~ не изменилось, объём кода, думаю, не сильно изментися, но теперь можно независимо задавать время таймаута нажатия клавиш и время удержания замка открытым.
А вообще и проверку кода, и запись нового - я бы выделил в static inline функции (просто static для компилятора, до сих пор не поддерживающего С99) - вменяемый оптимизатор даже просто static функцию проинлайнил бы по месту и объём кода не изменился бы, а "читабельность" выросла бы.
Да и часть комментариев ушла бы за ненадобностью, так как
Код
    else
        write_new_code_to_eeprom()

столь же понятно, как и
Код
    else {
        //------------------------------ Запись кода в EEPROM
DASM
OFF - ReAL - а как код в трубочку свернули ?
ReAl
Цитата(dxp @ Oct 24 2007, 09:19) *
Существует очень мало ситуаций, где этот оператор действительно необходим. В ~90% случаев это выход из вложенных циклов.
Ещё в 9% случаев - слияния разных ветвей case в общие: после некоторых разных предварительных операций в относительно длинную общую часть - тогда для сопровождения проще не дублировать эту общую часть (а потом забыть одну из них модифицировать), а поставить несколько goto. Но это для "избранных" мест, старательно вылизываемого "где-то-библиотечного" кода.
Иногда - сведение ветвей многоступенчатых if, но это почти то же самое.

А вот в приведенной программе goto явно от неумения писать на С, а не от большого умения :-)



Цитата(DASM @ Oct 24 2007, 13:49) *
OFF - ReAL - а как код в трубочку свернули ?
Тег CODEBOX вместо CODE
ValBag
Перечитал всю тему. На мой взгляд отказ от применения GoTo это стародавняя аксиома, которая в большинстве случаев многими принимается на веру. Тот же Страуструп (из топика #11) пишет:
Цитата
Кроме того, операторы goto могут пригодиться в тех случаях,
когда на первый план выходит скорость работы программы. Один из
них - когда в реальном времени происходят какие-то вычисления во
внутреннем цикле программы.
Есть немногие ситуации и в обычных программах, когда применение
goto оправдано. Одна из них - выход из вложенного цикла или
переключателя. Дело в том, что оператор break во вложенных циклах
или переключателях позволяет перейти только на один уровень выше.

Не считайте меня назойливым, еще раз обращаюсь к корифеям. Отредактируйте, пожалуйста текст программы (из топика #44) с пресловутым GoTo, чтобы она работала с другими операторами. Хочу увидеть разницу и приобрести некоторый опыт на живом примере. Ответы "сделай сам" не хотелось бы принимать. Все учились на чужих и своих примерах.
Спасибо!
DASM
А что по вашему ReAl сделал ?
ReAl
Цитата(ValBag @ Oct 24 2007, 14:06) *
Отредактируйте, пожалуйста текст программы (из топика #44) с пресловутым GoTo, чтобы она работала с другими операторами.
А чем не нравится мой вариант? Нужны шашечки или ехать? Я переписал не изменив "внешнюю" логику программы, но без goto, кто сказал, что оно должно выглядеть очень похоже на старое? Дословный перевод с языка на язык может либо исказить мысль больше, чем "правильный" перевод, либо выглядеть коряво. А тут мы переводим текст с чего-то типа бейсик-программы, записанной словами С на собственно С smile.gif

Тем более, что изменён только набор кода с клавиатуры, оставлено даже его поведение таким, как было, просто антидребезг и проверка нажатия новой клавиши реализованы несколько по-другому - гибче и расширяемее. Заодно убрана эту ужасающая задержка через wait() с "магическими числами". Всё остальное просто переписано без goto.
Ну вот "тупо" переведённый из goto-шного варианта в не-goto-шный:
CODE
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.4 Standard
Automatic Program Generator
© Copyright 1998-2004 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
e-mail:office@hpinfotech.com

Project : Пример 10
Version : 1
Date : 07.03.2006
Author : Belov
Company : Home
Comments:
Кодовый замок


Chip type : ATtiny2313
Clock frequency : 4,000000 MHz
Memory model : Tiny
External SRAM size : 0
Data Stack size : 32
*****************************************************/

#include <tiny2313.h>
#define klfree 0x77F // Код состояния при полностью отпущеных кнопках
#define kzad 3000 // Код задержки при сканировании
#define kandr 30 // Константа антидребезга
#define bsize 30 // Размер буфера для хранения кода

unsigned char flz; // Флаг задержки
unsigned int bufr[bsize]; // Буфер в ОЗУ для хранения кода
#pragma warn-
eeprom unsigned char klen; // Ячейка для хранения длины кода
eeprom unsigned int bufe[bsize]; // Буфер в EEPROM для хранения кода
#pragma warn+

// Прерывание по переполнению Таймера 1
interrupt[TIM1_OVF]
void timer1_ovf_isr(void)
{
flz = 1; // Устанавливаем флаг задержки
}

// Прерывание по совпадению в канале A Таймера 1
interrupt[TIM1_COMPA]
void timer1_compa_isr(void)
{
flz = 1; // Устанавливаем флаг задержки
}

// Функция опроса клавиатуры и антидребезга
unsigned int incod(void)
{
unsigned int cod0 = 0; // Локальные переменные
unsigned int cod1;
unsigned char k;

for (k = 0; k < kandr; k++) { // Цикл антидребезга
cod1 = PINB & 0x7; // Формируем первый байт кода
cod1 = (cod1 << 8) + (PIND & 0x7F); // Формируем полный код состояния клавиатуры
if (cod0 != cod1) { // Сравниваем со старым кодом
k = 0; // Если не равны, сбрасываем счетчик
cod0 = cod1; // И присваиваем новое значение старому коду
}
}
return cod1;
}


// Процедура формирования задержки
void wait(unsigned char kodz)
{
if (kodz == 1)
TIMSK = 0x40; // Выбор маски прерываний по таймеру
else
TIMSK = 0x80;
TCNT1 = 0; // Обнуление таймера
flz = 0; // Сброс флага задержки
#asm("sei"); // Разрешаем прерывания
if (kodz != 2)
while (flz == 0); // Цикл задержки
}


// Основная функция
void main(void)
{
unsigned char ii; // Указатель массива
unsigned char i; // Вспомогательный указатель
unsigned int codS; // Старый код

PORTB = 0xE7; // Порт B
DDRB = 0x18;

PORTD = 0x7F; // Порт D
DDRD = 0x00;

TCCR1A = 0x00; // Таймер/Счетчик 1
TCCR1B = 0x03;
TCNT1 = 0;
OCR1A = kzad;

ACSR = 0x80; // Аналоговый компаратор

while (1) { // Главный цикл
while (incod() != klfree); // Ожидание отпускания кнопок
while (incod() == klfree); // Ожидание нажатия кнопок
ii = 0;

// набор кода до конца буфера либо до таймаута между клавишами
while(1) {
#asm("cli"); // Запрещаем прерывания
wait(1); // Задержка 1-го типа
codS = incod(); // Ввод кода и запись, как старого
bufr[ii++] = codS; // Запись очередного кода в буфер
if (ii >= bsize) // Проверка конца буфера
break;

wait(2); // Задержка 2-го типа
while( flz == 0) { // пока не закончился интервал
if (incod() != codS) // Проверка не изменилось ли состояние
continue; // переход на набор кода
}
break; // таймаут - конец набора
}

// реакция на набранный код
if (PINB.7 == 1) { // Проверка переключателя режимов
//------------------------------ Проверка кода
if (klen != ii) // Проверка длины кода
continue; // главный цикл
for (i = 0; i < ii; i++)
if (bufe[i] != bufr[i]) // Проверка самого кода
continue; // главный цикл
} else { //------------------------------ Запись кода в EEPROM
klen = ii; // Запись длины кода
for (i = 0; i < ii; i++)
bufe[i] = bufr[i]; // Запись всех байтов кода
}

//------------------------------ Открывание замка
PORTB .4 = 1;
// Открываем замок
wait(3); // Задержка 3-го типа
PORTB .4 = 0; // Закрываем замок
}
}


p.s. сам я к этому вопросу не отношусь "религиозно" - goto применяю, но только если он действительно улучшает ситуацию.
ValBag
Цитата(DASM @ Oct 24 2007, 20:09) *
А что по вашему ReAl сделал ?

Прошу прощения, я его увидел после посылки своего предыдущего сообщения.
Проработаю. Спасибо ReAl
ReAl
Цитата(ValBag @ Oct 24 2007, 14:06) *
Тот же Страуструп (из топика #11) пишет:
Ну и где в приведённой программе то, о чём писал Страуструп (что-то очень быстрое или выход из нескольких вложенных циклов) ???
defunct
Цитата(ValBag @ Oct 24 2007, 09:19) *
Для примера в программе "кодовый замок" из книги Белова А.В., в небольшом тексте программы на CИ в модуле main 7 переходов. И все работает нормально.

Программа содержит аж 2 артефакта, первый конечно же goto, а второй - переменная "ii", давненько уже такого не видал smile.gif
ReAl
Раз уж я тут застрял - что я имел ввиду под "ужасающим wait()"
Функция должна делать более-менее однотипную работу.
Поэтому передавать в функцию параметр, меняющий время задержки - это нормально, но нагружать эту же функцию только инициализацией таймаута, который будет проверяться в другом месте и другим образом - это сделать код плохо читаемым (то же относится к макросам в ассемблере).
На скорую руку предлагаемые изменения выглядят так:
CODE

typedef enum { delay_50ms, delay_1s } delay_code_t;
// Процедура формирования задержки
void init_timer(delay_code_t dly )
{
if (dly == delay_50ms)
TIMSK = (1 << OCIE1A); // Выбор маски прерываний по таймеру
else
TIMSK = (1 << TOIE1);
TCNT1 = 0; // Обнуление таймера
flz = 0; // Сброс флага задержки
#asm("sei"); // Разрешаем прерывания
}

#define init_timeout() init_timer(delay_1s)
#define is_expired() (flz==1)

void wait(delay_code_t dly )
{
init_timer( dly);
while ( ! is_expired() ); // Цикл задержки
}


// Основная функция
void main(void)
{
........

while (1) { // Главный цикл
.........
// набор кода до конца буфера либо до таймаута между клавишами
while(1) {
#asm("cli"); // Запрещаем прерывания
wait(delay_50ms); // Задержка 1-го типа
codS = incod(); // Ввод кода и запись, как старого
bufr[ii++] = codS; // Запись очередного кода в буфер
if (ii >= bsize) // Проверка конца буфера
break;

init_timeout(); // Задержка 2-го типа
while( ! is_expired() ) { // пока не закончился интервал
if (incod() != codS) // Проверка не изменилось ли состояние
continue; // переход на набор кода
}
break; // таймаут - конец набора
}

.............
//------------------------------ Открывание замка
PORTB .4 = 1;
// Открываем замок
wait(delay_1s); // Задержка 3-го типа
PORTB .4 = 0; // Закрываем замок
}
}


Прошу прощения, на ходу кое-что поменял в уже отправленном коде.

Цитата(defunct @ Oct 24 2007, 14:42) *
Программа содержит аж 2 артефакта, первый конечно же goto, а второй - переменная "ii", давненько уже такого не видал smile.gif
Ото ж я быстренько-быстренько оставил только i как "временный" счётчик цикла, а вторую переименовал в осмысленный key_index, чтобы в глазах от i ii iii не рябило smile.gif
ValBag
ReAl
Цитата
А тут мы переводим текст с чего-то типа бейсик-программы, записанной словами С на собственно С

Неужели я применяю такой древний компилятор (такой же как в приложенном примере, кстати не моем, а указанного автора) который никто не знает. CodeVisionAVR это компилятор языка С http://www.hpinfotech.com У меня нет конкретного учителя. Подходящий для начинающих (по информации в литературе для новичков и ссылкам в сети) это CVAVR. Большинство утверждают, что самый лучший IAR, но по нему информации пользователя на русском нет, а уменя проблемы с английским. Вот такие дела! Спасибо за варианты, проработаю позже. С лету, как начинающий, не могу сразу оценить.

defunct А чему противоречит название переменной ii?
defunct
Цитата(ValBag @ Oct 24 2007, 16:09) *
А чему противоречит название переменной ii?

Лишь только читаемости кода, которая приводит в последствии к страшным глюкам

сравните:
Код
ii += 1;
...
for(i = 0; i < ii; i++)



Код
i += 1;
...
for(i = 0; ii < ii; i++)


Код
ii += 1;
...
for(i = 0; i < ii; ii++)
ValBag
defunct Понятно, появляется рябь в глазах, а затем в коде.
Спасибо, учту в дальнейшем.
Попутно еще один вопрос. Не подскажете, можно ли найти толковое руское описание IAR? А то все меня пристыдили с CodeVisionAVR.
alexander55
Цитата(ValBag @ Oct 24 2007, 17:33) *
defunct Понятно, появляется рябь в глазах, а затем в коде.
Спасибо, учту в дальнейшем.
Попутно еще один вопрос. Не подскажете, можно ли найти толковое руское описание IAR? А то все меня пристыдили с CodeVisionAVR.

Мне попалась книга П.П. Редькин "Микроконтроллеры ARM7 семейства LPC2000". Там страниц 100 про IAR. Где-то в интернете есть.
ReAl
Цитата(ValBag @ Oct 24 2007, 15:09) *
ReAl
Неужели я применяю такой древний компилятор (такой же как в приложенном примере, кстати не моем, а указанного автора) который никто не знает.
Нет, при чём тут "древний"?

http://www.lib.ru/ANEKDOTY/non_pas.txt
Цитата
- поскольку в Фортране отсутствуют структурные операторы IF, REPEAT ... UNTIL или CASE, настоящим программистам не нужно беспокоиться, что они их не используют; кроме того эти операторы можно при необходимости симулировать с помощью присваиваемых GOTO.
...
Да и потом, закоренелый настоящий программист может написать фортрановскую программу на любом языке.
Именно это я имел ввиду - пример от "указанного автора" написан, как я сказал, "словами С но на другом языке". Довольно, на мой взгляд, грязный код, там кроме goto и ii есть что поругать (два прерывания там, где можно было обойтись одним - даже если писать не так, как написал я), лишние (с непонятной смысловой нагрузкой) вызовы inkey(), использование "магических чисел" там, где для упрощения понимания написанного и будущего развития надо применять именованные вещи, будь-то #define или enum.

К "свежести" используемого Вами С-компилятора это не имеет никакого отношения, я лично никого не "пристыдил" - я вообще только понаслышке знаю - кто такие ImageCraft-ы с CodeVision-ами, мне и gcc хватает smile.gif
ValBag
Цитата(ReAl @ Oct 24 2007, 21:46) *
пример от "указанного автора" написан, как я сказал, "словами С но на другом языке".

"Указанный автор" это Белов А.В. Книга называется "Создаем устройства на микроконтроллерах". Это единственная книга, которую я нашел, с законченными примерами. Причем все примеры вначале приведены на ассемблере, а затем на С. Наверное этим и объясняется GoTO. Несмотря на это, кое что полезное я из нее извлек.
ValBag
REAL
Шашечек конечно не надо, но и ехать не получается. Глохнем на полпути. Это я так,
беззлобно, скорее в отношении себя.

1. Общие принципы избавления от GoTo мне понятны, но не до конца. Самое проблемное место,
переход из вложенных циклов вверх, более чем на начало вложенного цикла. Во втором и третьем Ваших вариантах первый оператор continue должен передаваь управление (по условию его действия) не на набор кода while(1), а на следующий цикл вложенного while(flz==0), в теле которого он появился? Буду рад, если ошибаюсь.
В первом варианте программы, еще не разобрался, комментарии Ваши не везде, а до Вашего мастерства мне далековато.

2. typedef enum {delay_50ms, delay_1s} delay_code. А typedef здесь зачем?, delay_code это
переменная объявленного перечисления с уже заданным типом.
ReAl
Цитата(ValBag @ Oct 25 2007, 14:45) *
Самое проблемное место, переход из вложенных циклов вверх, более чем на начало вложенного цикла. Во втором и третьем Ваших вариантах первый оператор continue должен передаваь управление (по условию его действия) не на набор кода while(1), а на следующий цикл вложенного while(flz==0), в теле которого он появился?
Тьху, это я ошибся. Конечно, оно идёт не туда, куда надо :-(
Ну плохо "втупую" переводится "спагетти" из goto в циклы. В первом варианте, когда я "понял, что нужно и написал почти с нуля", надеюсь, ошибок нет :-)
А тут надо что-то в таком духе рожать:
Код
        init_timeout();    // Задержка 2-го типа
        while( incod() == codS && !is_expired() ); // пока клавиша не изменилась и таймаут не истёк - ждём
        if( is_expired() ) break; // если таки таймаут - конец набора
    } // это от while(1) "набор кода"


Цитата(ValBag @ Oct 25 2007, 14:45) *
2. typedef enum {delay_50ms, delay_1s} delay_code. А typedef здесь зачем?, delay_code это переменная объявленного перечисления с уже заданным типом.
Нет, если бы не было слова typedef, то это была бы переменная типа неименованного enum с таким-то набором.
А так это перечислимый тип - определяется typedef-ом для того, чтобы не писать лишний раз слово enum везде, где нужно завести переменную этого типа. Вместо
Код
enum moo { moo0, moo1 }; // только объявляем тип, не заводя переменных
void foo( enum moo m) // аргумент этого типа
{
   if( m == moo1) { ...
пишем
Код
typedef enum { moo0, moo1 } moo_t; // тоже объявляем тип, но имя типа теперь из одного слова, без ключевого слова enum
void foo( moo_t m) // аргумент этого типа
{
   if( m == moo1) { ...
ValBag
ReAl
Теперь все понятно! Только наверное надо немного изменить этот оператор?
while ((incod() == codS) && (!is_expired()));
Спасибо за помощь!
ReAl
Цитата(ValBag @ Oct 27 2007, 10:58) *
Только наверное надо немного изменить этот оператор?
while ((incod() == codS) && (!is_expired()));
Тут эти скобки не обязательны - согласно приоритету операций. А лишние скобки только мешают читать, особенно тут возле !is_expired()
Никто же не пишет ((a*b)+(c*d))


p.s. Да, это надо привыкнуть, что && и || имеют более низкий приоритет, чем == != > и т.д., а + - * / имеют более высокий, чем == != и т.д.
Но ведь в школе же привыкли к приоритетам арифметических операций :-)
Тем более, что логика в этих приоритетах есть, если словами сказать "если код не равен старому И не закончилось время" - это ведь в русском языке означаент не "если код не равен (старому и не закончилось)", а таки "(если не равен старому) и (не закончилось)"

Единственное, пожалуй, неудобное место по приоритетам операций в С - это соотношение сложения и сдвига
a << 5 + 1
означает
a << (5 + 1)
а не
(a << 5) + 1
По крайней мере мне кажется более естественным размещение приоритета сдвигов между умножением-делением и сложением-вычитанием, а не ниже сложения-вычитания, но K&R решили по-другому и сдвиг надо брать в скобки.
sensor_ua
Цитата
Тут эти скобки не обязательны - согласно приоритету операций. А лишние скобки только мешают читать

А я никак не могу запомнить - приоритеты в группах действий арифметических, логических побитовых и логических завсегда помню, а прибавить к этому всяко-разно - лучше скобки поставитьwink.gif
SasaVitebsk
Цитата(ReAl @ Oct 24 2007, 14:55) *
Ещё в 9% случаев - слияния разных ветвей case в общие: после некоторых разных предварительных операций в относительно длинную общую часть - тогда для сопровождения проще не дублировать эту общую часть (а потом забыть одну из них модифицировать), а поставить несколько goto. Но это для "избранных" мест, старательно вылизываемого "где-то-библиотечного" кода.
Иногда - сведение ветвей многоступенчатых if, но это почти то же самое.


Мне как-то редко приходится использовать много вложенных циклов и я не объединяю (посредством GOTO) case-ы (сам IAR достаточно хорошо их объединяет).

В то же время изредка пользуюсь GOTO по причине которая уже указывалась. Обработка исключительных ситуаций. Причём в Pascal это используется чаще так как в Си можно сделать дополнительный return. Хотя этим нарушается ещё одно "золотое правило": Для каждой процедуры - один вход - один выход.

Конечно прямая обработка исключений - красивый выход как в С++ так и в Pascal, но иногда банально не хочется городить огород. К тому же я тоже считаю что основная причина - хорошая читаемость текста. С этой точки зрения вот такой текст к примеру - вполне укладывается
Код
procedure TForm1.mnuWriteClick(Sender: TObject);
label ExitSend;
const
       SmallTime     = 20;
       BigTime       = 400;
var
....
      if CountRep>3 then begin
          StatusBar1.Panels.Items[0].Text:= 'Ошибка! Кластер с запрошенным номером не отвечает';
          goto  ExitSend;
      end;
.....
        if CountRep>3 then begin
          StatusBar1.Panels.Items[0].Text:= 'Ошибка! Превышено количество повторов во время передачи';
          goto  ExitSend;
        end;
....
ExitSend:
  FComDriver.Active := false;
  FComDriver.Free;
  FComSettings.Free;
  StatusBar1.Panels.Items[0].Text:= '';
  Form1.StatusBar1.Panels.Items[1].Text:= '';
end
....


Сори - на Си не нашёл.

Понятно что всё это можно флагами сделать. Но нагляднее ли это будет?

На си решается так
Код
uint8_t    GetChar(void)
{
uint8_t        c;

if(Flag.SaveRollik) return    0;                            // Если идёт запись ролика, то выйти
c=GetCharInStream();
if(c != METKA) return c;
c=GetCharInStream();                                    // Взять следующий символ
if(c != METKA){
   Flag.ErrLoadKom = 1;                                    // Ошибка приёма данных, - пришла команда
   Kom = c;                                                // Досрочно
}
return    c;
}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.