Чтобы огласить весь список, нужно пересмотреть всё множество компиляторов с языка. Если Вам не понравилось слово "Многие", то можете заменить его на "Некоторые". Но, на мой взгляд, сути это не меняет, в общем случае организация цикла do while() и результирующий код для него являются самыми простыми.
ARV Вы привели частный пример задачи, решаемой с помощью трёх типов цикла, я расписал общий случай. Естественно, что в результате оптимизации таких простых циклов, генерируемый код имеет одинаковый размер. Противоречий не вижу. И оспаривать этот пример я, конечно, не буду.
Alex, какой из трёх вариантов, приведённых в примере ARV, Вы считаете "необходимо" использовать? Спрашиваю потому, что когда писал, то думал, что написать: "следует" или "лучше".
циклы while и for обладают тем приятным свойством, что в них проверка окончания осуществляется в начале, а не в конце цикла. Третий оператор цикла языка C, do-while, проверяет условие окончания в конце, после каждого прохода через тело цикла; тело цикла всегда выполняется по крайней мере один раз. Как и можно было ожидать, цикл do-while используется значительно реже, чем while и for, составляя примерно пять процентов от всех циклов. Тем не менее, иногда он оказывается полезным, как, например, в следующей функции itoa, которая преобразует число в символьную строку (обратная функции atoi). Эта задача оказывается несколько более сложной, чем может показаться сначала. Дело в том, что простые методы выделения цифр генерируют их в неправильном порядке. Мы предпочли получить строку в обратном порядке, а затем обратить ее.
Код:
/* конвертировать n в строку s */ itoa(n, s) char s[]; int n; { int i, sign;
if((sign = n) < 0) /* сохранить знак */ n = -n; /* сделать n положительным */ i = 0; do { /* генерируем цифры в обратном порядке */ s[i++] = n % 10 + '0'; /* получить следующую цифру */ } while((n /= 10) > 0); /* удалить ее */ if(sign < 0) s[i++] = '-' s[i] = '\0'; reverse(s); }
Цитата:
Цикл do-while здесь необходим, или по крайней мере удобен, поскольку, каково бы ни было значение n, массив s должен содержать хотя бы один символ. Мы заключили в фигурные скобки один оператор, составляющий тело do-while, хотя это и не обязательно, для того, чтобы торопливый читатель не принял часть while за начало оператора цикла while.
void f3(void) { for( c=0; a>b; b++ ) { c += b; } } //---------- void main(void) { a = 10; b = 2; f1(); //--------- a = 10; b = 2; f2(); //--------- a = 10; b = 2; f3();
}
и прокомпилируем "классикой" BC 3.1, то получим: Спойлер
Код:
; ; void f1(void) ; assume cs:_TEXT _f1 proc near push bp mov bp,sp ; ; { ; c = 0; ; mov byte ptr DGROUP:_c,0 @1@58: ; ; do ; { ; c += b; ; mov al,byte ptr DGROUP:_b add byte ptr DGROUP:_c,al ; ; b++; ; inc byte ptr DGROUP:_b ; ; }while( a>b ); ; mov al,byte ptr DGROUP:_a cmp al,byte ptr DGROUP:_b ja short @1@58 ; ; } ; pop bp ret _f1 endp ; ; void f2(void) ; assume cs:_TEXT _f2 proc near push bp mov bp,sp ; ; { ; c=0; ; mov byte ptr DGROUP:_c,0 jmp short @2@86 @2@58: ; ; while( a>b ) ; { ; c += b; ; mov al,byte ptr DGROUP:_b add byte ptr DGROUP:_c,al ; ; b++; ; inc byte ptr DGROUP:_b @2@86: mov al,byte ptr DGROUP:_a cmp al,byte ptr DGROUP:_b ja short @2@58 ; ; } ; } ; pop bp ret _f2 endp ; ; void f3(void) ; assume cs:_TEXT _f3 proc near push bp mov bp,sp ; ; { ; for( c=0; a>b; b++ ) ; mov byte ptr DGROUP:_c,0 jmp short @3@114 @3@58: ; ; { ; c += b; ; mov al,byte ptr DGROUP:_b add byte ptr DGROUP:_c,al inc byte ptr DGROUP:_b @3@114: mov al,byte ptr DGROUP:_a cmp al,byte ptr DGROUP:_b ja short @3@58 ; ; } ; } ; pop bp ret _f3 endp
и увидим, во втором и третьем случае те самые переходы на проверку условия "jmp short @2@86" и "jmp short @3@114"
Теперь прокомпилируем тоже самое компилятором SDCC под "процессор" 8051 и получим код: Спойлер
Код:
;---------- ;Allocation info for local variables in function 'f1' ;---------- ; 8051test.c:6: void f1(void) ; ---------- ; function f1 ; ---------- _f1: ar7 = 0x07 ar6 = 0x06 ar5 = 0x05 ar4 = 0x04 ar3 = 0x03 ar2 = 0x02 ar1 = 0x01 ar0 = 0x00 ; 8051test.c:8: c = 0; mov _c,#0x00 ; 8051test.c:9: do 00101$: ; 8051test.c:11: c += b; mov a,_b add a,_c mov _c,a ; 8051test.c:12: b++; inc _b ; 8051test.c:13: }while( a>b ); clr c mov a,_b subb a,_a jc 00101$ ret ;---------- ;Allocation info for local variables in function 'f2' ;---------- ; 8051test.c:16: void f2(void) ; ---------- ; function f2 ; ---------- _f2: ; 8051test.c:18: c=0; mov _c,#0x00 ; 8051test.c:19: while( a>b ) 00101$: clr c mov a,_b subb a,_a jnc 00104$ ; 8051test.c:21: c += b; mov a,_b add a,_c mov _c,a ; 8051test.c:22: b++; inc _b sjmp 00101$ 00104$: ret ;---------- ;Allocation info for local variables in function 'f3' ;---------- ; 8051test.c:26: void f3(void) ; ---------- ; function f3 ; ---------- _f3: ; 8051test.c:28: for( c=0; a>b; b++ ) mov _c,#0x00 00103$: clr c mov a,_b subb a,_a jnc 00105$ ; 8051test.c:30: c += b; mov a,_b add a,_c mov _c,a ; 8051test.c:28: for( c=0; a>b; b++ ) inc _b sjmp 00103$ 00105$: ret
Здесь тоже во втором и третьем случае видим дополнительные переходы "jnc 00104$" и "jnc 00105$" с единственным отличием - проверку условия компилятор размещает перед, а не после тела цикла.
Последний раз редактировалось Const14 Пт окт 27, 2017 15:17:15, всего редактировалось 1 раз.
Немножко странный код в том смысле, что коль скоро глобальная переменная b изменяется в каждой функции, так что на входы второй и третьей приходят уже разные данные.
Да и первая функция считает чуть по-другому, чем вторая и третья
Код не имеет какого-либо практичского назначения, не ищите в нём смысл, вызовы функций f1, f2, f3 формальны. Немного поправил, чтобы уж совсем странно не выглядело. Первая функция иллюстрирует цикл do ... while() и генерируемый для него код. Ещё раз повторю, что использовать его можно только в том случае, если тело цикла должно выполниться хотя-бы один раз!
Дык я об этом и сказал: do..while всегда быстрее или такой же как и while(). В простых циклах, когда компилятору удаётся превратить while() в do {} while, то разницы не будет. Если подумать головой и посмотреть на код, то должно стать понятно и так, что while() медленнее.
Цитата:
весь сыр-бор из-за жалкого jmp, которого иногда может и не быть
да-да-да. А потом удивляемся кто же пишет всё гавённое тормозящее ПО.
Спасибо всем за исчерпывающую дискуссию. Есть вопрос о порядке/приоритетности анализа условий. Допустим есть код:
Код:
if ((mode == ALARM_ACTIVE) || (mode == ALARM_COMPL)) { // do something }
И событие ALARM_ACTIVE всегда предшествует ALARM_COMPL (так построена логика + состояние устанавливаеться 2 источниками ALARM_ACTIVE, а переход к следующему идет только 1, так что само состояние ALARM_COMPL вроде как встречаеться реже). Правильно ли я понимаю, что в таком случае целесообразнее их поменять местами, потому что компилятор "читает" условия справа-налево, и таким образом немного ускорить обработку этого условия ?
if ((mode == ALARM_ACTIVE) || (mode == ALARM_COMPL)) { // do something }
Внутренние скобки - лишние, поскольку приоритет оператора == равен 7 а у оператора || равен 12. Таблицу приоритетов можно распечатать и заламинировать - чтобы она всегда была перед глазами, а код был чистый и аккуратный.
И событие ALARM_ACTIVE всегда предшествует ALARM_COMPL (так построена логика + состояние устанавливаеться 2 источниками ALARM_ACTIVE, а переход к следующему идет только 1, так что само состояние ALARM_COMPL вроде как встречаеться реже). Правильно ли я понимаю, что в таком случае целесообразнее их поменять местами, потому что компилятор "читает" условия справа-налево, и таким образом немного ускорить обработку этого условия ?
Кто про что - а я вновь про таблицу приоритетов - ассоциативность у обоих используемых операторов "слева-направо". Так что событие ALARM_COMPL будет проверяться только если не произошло ALARM_ACTIVE а не наоборот, как вам нашёптывают "бесы целесообразности".
_________________ Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR!
а я всегда сам придерживался правила и другим советовал: скобки лишними не бывают. тем более что в MISRA вообще запрещается надеяться на приоритеты операторов...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Карма: 90
Рейтинг сообщений: 1432
Зарегистрирован: Чт мар 18, 2010 23:09:57 Сообщений: 4598 Откуда: Планета Земля
Рейтинг сообщения:0 Медали: 1
Пока_без_кота писал(а):
А я уже поудалял
Ну и напрасно. Не стоит идти против своего удобства, всего-лишь по какому-то совету с форума. Тут дело лично каждого. А компилятору - пофиг. Я, например, тоже придерживаюсь принципа "скобки лишними не бывают". Во-первых, что самое главное, исключены ошибочные записи. Мы пишем так, как выражение лежит у нас в голове. Во-вторых, улучшается читабельность кода. Ну а какие плюсы, кроме минусов, от отсутствия "лишних" скобок - мне неведомо
Внутренние скобки - лишние, поскольку приоритет оператора == равен 7 а у оператора || равен 12.
Машинный код для машины, исходный - для человека. Поддерживаю предыдущих ораторов в принципе - скобок много не бывает. Автор или другой человек может читать исходный код и забыть о приоритетах, при беглом осмотре это тоже будет притормаживать внимание; разработчики ныне знают более одного языка программирования, вместо кучи памяток о приоритетах можно просто взять и поставить скобки; программирование => математика и эстетичнее выглядит заключённое в скобке выражение - тешит внутреннего перфекциониста.
Как говорили: для C/C++ определён порядок слева-направо и перестановкой действительно можно добиться ускорения программы для частых условий.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения