Чтобы огласить весь список, нужно пересмотреть всё множество компиляторов с языка. Если Вам не понравилось слово "Многие", то можете заменить его на "Некоторые". Но, на мой взгляд, сути это не меняет, в общем случае организация цикла 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
Рейтинг сообщений: 1443
Зарегистрирован: Чт мар 18, 2010 23:09:57 Сообщений: 4613 Откуда: Планета Земля
Рейтинг сообщения:0 Медали: 1
Пока_без_кота писал(а):
А я уже поудалял
Ну и напрасно. Не стоит идти против своего удобства, всего-лишь по какому-то совету с форума. Тут дело лично каждого. А компилятору - пофиг. Я, например, тоже придерживаюсь принципа "скобки лишними не бывают". Во-первых, что самое главное, исключены ошибочные записи. Мы пишем так, как выражение лежит у нас в голове. Во-вторых, улучшается читабельность кода. Ну а какие плюсы, кроме минусов, от отсутствия "лишних" скобок - мне неведомо
Внутренние скобки - лишние, поскольку приоритет оператора == равен 7 а у оператора || равен 12.
Машинный код для машины, исходный - для человека. Поддерживаю предыдущих ораторов в принципе - скобок много не бывает. Автор или другой человек может читать исходный код и забыть о приоритетах, при беглом осмотре это тоже будет притормаживать внимание; разработчики ныне знают более одного языка программирования, вместо кучи памяток о приоритетах можно просто взять и поставить скобки; программирование => математика и эстетичнее выглядит заключённое в скобке выражение - тешит внутреннего перфекциониста.
Как говорили: для C/C++ определён порядок слева-направо и перестановкой действительно можно добиться ускорения программы для частых условий.
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения