реклама на сайте
подробности

 
 
6 страниц V  « < 3 4 5 6 >  
Reply to this topicStart new topic
> Оператор GoTo, безусловный переход или правила хорошего тона
ReAl
сообщение Oct 24 2007, 11:46
Сообщение #61


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(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


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
DASM
сообщение Oct 24 2007, 11:49
Сообщение #62


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



OFF - ReAL - а как код в трубочку свернули ?
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 24 2007, 11:55
Сообщение #63


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



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

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



Цитата(DASM @ Oct 24 2007, 13:49) *
OFF - ReAL - а как код в трубочку свернули ?
Тег CODEBOX вместо CODE


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
ValBag
сообщение Oct 24 2007, 12:06
Сообщение #64


Частый гость
**

Группа: Участник
Сообщений: 91
Регистрация: 15-03-07
Пользователь №: 26 183



Перечитал всю тему. На мой взгляд отказ от применения GoTo это стародавняя аксиома, которая в большинстве случаев многими принимается на веру. Тот же Страуструп (из топика #11) пишет:
Цитата
Кроме того, операторы goto могут пригодиться в тех случаях,
когда на первый план выходит скорость работы программы. Один из
них - когда в реальном времени происходят какие-то вычисления во
внутреннем цикле программы.
Есть немногие ситуации и в обычных программах, когда применение
goto оправдано. Одна из них - выход из вложенного цикла или
переключателя. Дело в том, что оператор break во вложенных циклах
или переключателях позволяет перейти только на один уровень выше.

Не считайте меня назойливым, еще раз обращаюсь к корифеям. Отредактируйте, пожалуйста текст программы (из топика #44) с пресловутым GoTo, чтобы она работала с другими операторами. Хочу увидеть разницу и приобрести некоторый опыт на живом примере. Ответы "сделай сам" не хотелось бы принимать. Все учились на чужих и своих примерах.
Спасибо!
Go to the top of the page
 
+Quote Post
DASM
сообщение Oct 24 2007, 12:09
Сообщение #65


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



А что по вашему ReAl сделал ?
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 24 2007, 12:27
Сообщение #66


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(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 применяю, но только если он действительно улучшает ситуацию.


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
ValBag
сообщение Oct 24 2007, 12:29
Сообщение #67


Частый гость
**

Группа: Участник
Сообщений: 91
Регистрация: 15-03-07
Пользователь №: 26 183



Цитата(DASM @ Oct 24 2007, 20:09) *
А что по вашему ReAl сделал ?

Прошу прощения, я его увидел после посылки своего предыдущего сообщения.
Проработаю. Спасибо ReAl
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 24 2007, 12:29
Сообщение #68


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(ValBag @ Oct 24 2007, 14:06) *
Тот же Страуструп (из топика #11) пишет:
Ну и где в приведённой программе то, о чём писал Страуструп (что-то очень быстрое или выход из нескольких вложенных циклов) ???


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
defunct
сообщение Oct 24 2007, 12:42
Сообщение #69


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(ValBag @ Oct 24 2007, 09:19) *
Для примера в программе "кодовый замок" из книги Белова А.В., в небольшом тексте программы на CИ в модуле main 7 переходов. И все работает нормально.

Программа содержит аж 2 артефакта, первый конечно же goto, а второй - переменная "ii", давненько уже такого не видал smile.gif
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 24 2007, 12:50
Сообщение #70


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Раз уж я тут застрял - что я имел ввиду под "ужасающим 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


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
ValBag
сообщение Oct 24 2007, 13:09
Сообщение #71


Частый гость
**

Группа: Участник
Сообщений: 91
Регистрация: 15-03-07
Пользователь №: 26 183



ReAl
Цитата
А тут мы переводим текст с чего-то типа бейсик-программы, записанной словами С на собственно С

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

defunct А чему противоречит название переменной ii?
Go to the top of the page
 
+Quote Post
defunct
сообщение Oct 24 2007, 13:16
Сообщение #72


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(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++)
Go to the top of the page
 
+Quote Post
ValBag
сообщение Oct 24 2007, 13:33
Сообщение #73


Частый гость
**

Группа: Участник
Сообщений: 91
Регистрация: 15-03-07
Пользователь №: 26 183



defunct Понятно, появляется рябь в глазах, а затем в коде.
Спасибо, учту в дальнейшем.
Попутно еще один вопрос. Не подскажете, можно ли найти толковое руское описание IAR? А то все меня пристыдили с CodeVisionAVR.
Go to the top of the page
 
+Quote Post
alexander55
сообщение Oct 24 2007, 13:44
Сообщение #74


Бывалый
*****

Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615



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

Мне попалась книга П.П. Редькин "Микроконтроллеры ARM7 семейства LPC2000". Там страниц 100 про IAR. Где-то в интернете есть.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Oct 24 2007, 13:46
Сообщение #75


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(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


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post

6 страниц V  « < 3 4 5 6 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 15:17
Рейтинг@Mail.ru


Страница сгенерированна за 0.03134 секунд с 7
ELECTRONIX ©2004-2016