Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18561
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

Const14 писал(а):Многие компиляторы
огласите весь список, пожалуйста
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Реклама
Const14
Родился
Сообщения: 18
Зарегистрирован: Чт авг 06, 2015 09:38:22
Откуда: Москва

Re: Вопросы по С/С++ (СИ)

Сообщение Const14 »

Чтобы огласить весь список, нужно пересмотреть всё множество компиляторов с языка.
Если Вам не понравилось слово "Многие", то можете заменить его на "Некоторые".
Но, на мой взгляд, сути это не меняет, в общем случае организация цикла do while() и результирующий код для него являются самыми простыми.
Реклама
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18561
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

Ну во всяком случае AVR-GCC 5.2.1 в ваш список не входит...

Код: Выделить всё

#include <avr/io.h>

int main(void){
	uint8_t i;

	// вариант 1 - 106 байт flash
	i = 12;
	while(i--){
		PORTB = PINC;
	}

	// вариант 2 - 104 байта flash
	for(i = 12; i; i--){
		PORTB = PORTC;
	}

	// вариант 3 - 104 байта flash
	i = 12;
	do{
		PORTB = PORTC;
		i--;
	} while(i);
}
в вышеприведенном тесте проводилась компиляция с оптимизацией -Os, при этом 2 из трех вариантов циклов, делающих одно и то же, заремаривались.

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

Мой уютный бложик... заходите!
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение Аlex »

Const14 писал(а):Но пользоваться лучше тем, который больше подходит для данного конкретного случая
Не "лучше", а "необходимо" !
Нужно понимать разницу между

Код: Выделить всё

do{}while();
,

Код: Выделить всё

while(){}
и

Код: Выделить всё

for(;;){}
Каждый вариант подходит для конкретного случая. Они не всегда взаимозаменяемы.
Реклама
Эиком - электронные компоненты и радиодетали
Const14
Родился
Сообщения: 18
Зарегистрирован: Чт авг 06, 2015 09:38:22
Откуда: Москва

Re: Вопросы по С/С++ (СИ)

Сообщение Const14 »

ARV
Вы привели частный пример задачи, решаемой с помощью трёх типов цикла, я расписал общий случай.
Естественно, что в результате оптимизации таких простых циклов, генерируемый код имеет одинаковый размер.
Противоречий не вижу. И оспаривать этот пример я, конечно, не буду.

Alex, какой из трёх вариантов, приведённых в примере ARV, Вы считаете "необходимо" использовать?
Спрашиваю потому, что когда писал, то думал, что написать: "следует" или "лучше".
Реклама
Аватара пользователя
oleg110592
Друг Кота
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Re: Вопросы по С/С++ (СИ)

Сообщение oleg110592 »

Отцы основатели (K&R) пишут:
циклы 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.
Реклама
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18561
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

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

Мой уютный бложик... заходите!
Const14
Родился
Сообщения: 18
Зарегистрирован: Чт авг 06, 2015 09:38:22
Откуда: Москва

Re: Вопросы по С/С++ (СИ)

Сообщение Const14 »

Собственно, я вообще проблемы не вижу и не могу понять, в чём Вы её видите.
Если мы возьмём пример:
Спойлер

Код: Выделить всё

unsigned char a,b,c;

//----------------------
void f1(void)
{
   c = 0;
   do
   {
      c += b;
      b++;
   }while( a>b );
}
   
void f2(void)
{
   c=0;
   while( a>b )
   {
      c += b;
      b++;
   }
}

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 раз.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18561
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

Const14 писал(а):я вообще проблемы не вижу и не могу понять, в чём Вы её видите
аналогично :) весь сыр-бор из-за жалкого jmp, которого иногда может и не быть :)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

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

Да и первая функция считает чуть по-другому, чем вторая и третья
Const14
Родился
Сообщения: 18
Зарегистрирован: Чт авг 06, 2015 09:38:22
Откуда: Москва

Re: Вопросы по С/С++ (СИ)

Сообщение Const14 »

Код не имеет какого-либо практичского назначения, не ищите в нём смысл, вызовы функций f1, f2, f3 формальны. Немного поправил, чтобы уж совсем странно не выглядело.
Первая функция иллюстрирует цикл do ... while() и генерируемый для него код. Ещё раз повторю, что использовать его можно только в том случае, если тело цикла должно выполниться хотя-бы один раз!
Аватара пользователя
smalcom
Встал на лапы
Сообщения: 128
Зарегистрирован: Пн фев 08, 2016 10:57:14

Re: Вопросы по С/С++ (СИ)

Сообщение smalcom »

на целых 2 байта хуже показал себя цикл while
Дык я об этом и сказал: do..while всегда быстрее или такой же как и while(). В простых циклах, когда компилятору удаётся превратить while() в do {} while, то разницы не будет.
Если подумать головой и посмотреть на код, то должно стать понятно и так, что while() медленнее.
весь сыр-бор из-за жалкого jmp, которого иногда может и не быть
да-да-да. А потом удивляемся кто же пишет всё гавённое тормозящее ПО.
Пока_без_кота
Потрогал лапой паяльник
Сообщения: 359
Зарегистрирован: Чт авг 08, 2013 01:06:54

Re: Вопросы по С/С++ (СИ)

Сообщение Пока_без_кота »

Спасибо всем за исчерпывающую дискуссию. Есть вопрос о порядке/приоритетности анализа условий. Допустим есть код:

Код: Выделить всё

if ((mode == ALARM_ACTIVE) || (mode == ALARM_COMPL))
{
     // do something
}
И событие ALARM_ACTIVE всегда предшествует ALARM_COMPL (так построена логика + состояние устанавливаеться 2 источниками ALARM_ACTIVE, а переход к следующему идет только 1, так что само состояние ALARM_COMPL вроде как встречаеться реже). Правильно ли я понимаю, что в таком случае целесообразнее их поменять местами, потому что компилятор "читает" условия справа-налево, и таким образом немного ускорить обработку этого условия ?
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

ЕМНИП, вообще-то слева направо.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

[uquote="Пока_без_кота",url="/forum/viewtopic.php?p=3218828#p3218828"]Есть вопрос о порядке/приоритетности анализа условий.[/uquote]
Есть таблица приоритетов и ассоциативности операторов - снимает напрочь все подобные вопросы, ознакомьтесь - не пожалеете! ;-)
[uquote="Пока_без_кота",url="/forum/viewtopic.php?p=3218828#p3218828"]Допустим есть код:

Код: Выделить всё

if ((mode == ALARM_ACTIVE) || (mode == ALARM_COMPL))
{
     // do something
}
[/uquote]
Внутренние скобки - лишние, поскольку приоритет оператора == равен 7 а у оператора || равен 12. Таблицу приоритетов можно распечатать и заламинировать - чтобы она всегда была перед глазами, а код был чистый и аккуратный.
[uquote="Пока_без_кота",url="/forum/viewtopic.php?p=3218828#p3218828"]И событие ALARM_ACTIVE всегда предшествует ALARM_COMPL (так построена логика + состояние устанавливаеться 2 источниками ALARM_ACTIVE, а переход к следующему идет только 1, так что само состояние ALARM_COMPL вроде как встречаеться реже). Правильно ли я понимаю, что в таком случае целесообразнее их поменять местами, потому что компилятор "читает" условия справа-налево, и таким образом немного ускорить обработку этого условия ?[/uquote]
Кто про что - а я вновь про таблицу приоритетов :) - ассоциативность у обоих используемых операторов "слева-направо". Так что событие ALARM_COMPL будет проверяться только если не произошло ALARM_ACTIVE а не наоборот, как вам нашёптывают "бесы целесообразности".
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Пока_без_кота
Потрогал лапой паяльник
Сообщения: 359
Зарегистрирован: Чт авг 08, 2013 01:06:54

Re: Вопросы по С/С++ (СИ)

Сообщение Пока_без_кота »

Спасибо, уже читаю :write:
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18561
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

Siarzhuk писал(а):Внутренние скобки - лишние
а я всегда сам придерживался правила и другим советовал: скобки лишними не бывают. тем более что в MISRA вообще запрещается надеяться на приоритеты операторов...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Пока_без_кота
Потрогал лапой паяльник
Сообщения: 359
Зарегистрирован: Чт авг 08, 2013 01:06:54

Re: Вопросы по С/С++ (СИ)

Сообщение Пока_без_кота »

Блин.. А я уже поудалял :)))
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение Аlex »

Пока_без_кота писал(а):А я уже поудалял
Ну и напрасно. Не стоит идти против своего удобства, всего-лишь по какому-то совету с форума. Тут дело лично каждого. А компилятору - пофиг.
Я, например, тоже придерживаюсь принципа "скобки лишними не бывают". Во-первых, что самое главное, исключены ошибочные записи. Мы пишем так, как выражение лежит у нас в голове. Во-вторых, улучшается читабельность кода.
Ну а какие плюсы, кроме минусов, от отсутствия "лишних" скобок - мне неведомо :dont_know:
Аватара пользователя
smalcom
Встал на лапы
Сообщения: 128
Зарегистрирован: Пн фев 08, 2016 10:57:14

Re: Вопросы по С/С++ (СИ)

Сообщение smalcom »

Внутренние скобки - лишние, поскольку приоритет оператора == равен 7 а у оператора || равен 12.
Машинный код для машины, исходный - для человека. Поддерживаю предыдущих ораторов в принципе - скобок много не бывает. Автор или другой человек может читать исходный код и забыть о приоритетах, при беглом осмотре это тоже будет притормаживать внимание; разработчики ныне знают более одного языка программирования, вместо кучи памяток о приоритетах можно просто взять и поставить скобки; программирование => математика и эстетичнее выглядит заключённое в скобке выражение - тешит внутреннего перфекциониста.

Как говорили: для C/C++ определён порядок слева-направо и перестановкой действительно можно добиться ускорения программы для частых условий.
Ответить

Вернуться в «Разные вопросы по МК»