Инициализация переменных

sadman41
Offline
Зарегистрирован: 19.10.2016

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

void test(uint8_t _nor){
 //uint8_t dropResult = 55;
 uint8_t dropResult;

 Serial.print("[1] dropResult: ");  Serial.println(dropResult);
 if (_nor == 17) { 
    dropResult = 8;
    Serial.println("[*] if");
 } 
 Serial.print("[2] dropResult: ");  Serial.println(dropResult);
}

void setup() {
 Serial.begin(9600);
 test(18);
}

void loop() {}

Так, как переменная dropResult не инициализируется (варнинги я видел), я ожидаю увидеть в первом Serial.println() произвольное значение, которое оставлено предыдущей функцией, получавшей эту ячейку памяти. И в том, что сразу после условия - точно такое же. Может, конечно, это наивное желание. Потому что вот, что показывает мне Serial Monitor:

[1] dropResult: 0
[2] dropResult: 8
 
Т.е. Serial.println() из if() не выполняется, а присвоение - вполне себе. При инициалилизации в момент объявления переменной такого эффекта нет.
 
Перепроверил код не раз. Перенес в CodeLite (в нем такой же компилятор, что и в Arduino IDE) - там всё нормально, в обоих строчках выводится значение переменной равное нулю.

Вопрос: что это - моё непонимание стандарта, криворукость, проблема с компилятором или проявление того самого пресловутого нечестного Си, после использования которого серебрянные ложки пропадают?

Logik
Offline
Зарегистрирован: 05.08.2014

Посмотред дизассемблер кода. Оптимизация сделала так

void test(uint8_t _nor){

 Serial.print("[1] dropResult: ");  Serial.println(0);
 if (_nor == 17) { 
    Serial.println("[*] if");
 } 
 Serial.print("[2] dropResult: ");  Serial.println(8);
}

Т.е. переменной вобще нету! Раз значение задается константой для одной из веток if а в других оно не определено, то для неопределенной ветки компилятор вправе выбрать любое ему понравившееся, а выбрав константу 8 можна  сократить код. Так он и сделал. Если в Вашем примере заменить стр.07 на  dropResult = 8+millis(); то компилятор лишается счастья сократить код константной оптимизацией, ему приходится заводить переменную dropResult которую он инитит 0, т.к. чем бы ни инитил - сократить не получится. Эффект пропадает и видим

[1] dropResult: 0

[2] dropResult: 0
 
ПС. Эффект интересный, только на самом адском собеседовании такие вопросы задавать.
 
 
sadman41
Offline
Зарегистрирован: 19.10.2016

Про право обходится с неинициализированной переменной как угодно я читал, конечно. Но меня смущал тот факт, что переменную я, в сущности, использовал до if() и под нее уже должна быть выделена память (по моим понятиям). А так же добивало то, что в CodeLite всё происходило корректно при том, что компилятор один и тот же. Получается, что так параметры оптимизации сказываются (видимо различны в обоих случаях)?

История-то эта началась с того, что я в каком-то англоязычном интернет-руководстве по оптимизации кода для микроконтроллеров прочитал, что те, кто инициализируют объявленные переменные нулем - зря растрачивают progmem space и собственное время. Решил проверить - верно ли такое утверждение для МК, который оказался под рукой...

UPD: и я не совсем понимаю, что такое для компилятора "неопределенная ветка [оператора if]", ведь указание на обязательное применение else отсутствует. В итоге  if() должен просто превратиться в JNE какой-нибудь, как я понимаю и не более того. Запись же в ячейку памяти не произвольного значения, а явно находящегося в невыполняемом блоке - уже за гранью добра и зла )) 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

sadman41 пишет:

меня смущал тот факт, что переменную я, в сущности, использовал до if() и под нее уже должна быть выделена память

Даже если так, и что? Никто Ва не гарантирует ни разумного значение в этой памяти, ни того, что она будет оставаться неизменной - там случайный мусор, уж какой получился.

sadman41 пишет:

в CodeLite всё происходило корректно 

Здесь тоже всё абсолютно корректно. Значение не определено и может быть каким угодно. В чём некорректность?

sadman41 пишет:

те, кто инициализируют объявленные переменные нулем - зря растрачивают progmem space и собственное время. 

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

С оптимизатором не всё так однозначно. Чаще всего он не будет вставлять код инициализации нулём, но и гадить в переменную не будет, вот всё хорошо и получится.

sadman41 пишет:

 Запись же в ячейку памяти не произвольного значения, а явно находящегося в невыполняемом блоке - уже за гранью добра и зла )) 

Никаких граней. Вы странно пнимаете выражение "произвольное значение". Чем по Вашему значение "явно находящееся в невыполняемом блоке" не произвольно? "Произвольное" значит, что там будет что-то, что туда по каким-то причинам попало и не более того.

 

Logik
Offline
Зарегистрирован: 05.08.2014

sadman41 пишет:

 переменную я, в сущности, использовал до if() и под нее уже должна быть выделена память (по моим понятиям). 

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

sadman41 пишет:

Получается, что так параметры оптимизации сказываются (видимо различны в обоих случаях)?

 

да.

sadman41 пишет:

История-то эта началась с того, что я в каком-то англоязычном интернет-руководстве по оптимизации кода для микроконтроллеров прочитал, что те, кто инициализируют объявленные переменные нулем - зря растрачивают progmem space и собственное время.

Да. Но это относится только к глобальным переменным, ну и статическим, которые по сути глобальные с ограниченой областью виденья. Компилятор собирает их в отдельную секцию, т.е. непрерывную область в памяти и при старте контроллера одним циклом записывает 0 во всю область. Локальные переменные надо всегда инитить ручками. Причем отсутствие их инициализации - источник большого кол-ва неприятных (т.к. иногда нестабильных и невоспроизводимых) ошибок.

sadman41 пишет:

UPD: и я не совсем понимаю, что такое для компилятора "неопределенная ветка [оператора if]", ведь указание на обязательное применение else отсутствует. 

именно она, отсутствующая, т.е. не определена.

sadman41 пишет:

 В итоге  if() должен просто превратиться в JNE какой-нибудь, как я понимаю и не более того.

ага. так. стр.16 только обход вызова Serial.println("[*] if");

000000c0 <_Z4testh>:
  c0:	1f 93       	push	r17
  c2:	18 2f       	mov	r17, r24
  c4:	82 ec       	ldi	r24, 0xC2	; 194
  c6:	91 e0       	ldi	r25, 0x01	; 1
  c8:	60 e0       	ldi	r22, 0x00	; 0
  ca:	71 e0       	ldi	r23, 0x01	; 1
  cc:	0e 94 b3 03 	call	0x766	; 0x766 <_ZN5Print5printEPKc>
  d0:	82 ec       	ldi	r24, 0xC2	; 194
  d2:	91 e0       	ldi	r25, 0x01	; 1
  d4:	60 e0       	ldi	r22, 0x00	; 0
  d6:	4a e0       	ldi	r20, 0x0A	; 10
  d8:	50 e0       	ldi	r21, 0x00	; 0
  da:	0e 94 95 03 	call	0x72a	; 0x72a <_ZN5Print7printlnEhi>
  de:	11 31       	cpi	r17, 0x11	; 17
  e0:	31 f4       	brne	.+12     	; 0xee <_Z4testh+0x2e>
  e2:	82 ec       	ldi	r24, 0xC2	; 194
  e4:	91 e0       	ldi	r25, 0x01	; 1
  e6:	61 e1       	ldi	r22, 0x11	; 17
  e8:	71 e0       	ldi	r23, 0x01	; 1
  ea:	0e 94 b6 03 	call	0x76c	; 0x76c <_ZN5Print7printlnEPKc>
  ee:	82 ec       	ldi	r24, 0xC2	; 194
  f0:	91 e0       	ldi	r25, 0x01	; 1
  f2:	68 e1       	ldi	r22, 0x18	; 24
  f4:	71 e0       	ldi	r23, 0x01	; 1
  f6:	0e 94 b3 03 	call	0x766	; 0x766 <_ZN5Print5printEPKc>
  fa:	82 ec       	ldi	r24, 0xC2	; 194
  fc:	91 e0       	ldi	r25, 0x01	; 1
  fe:	68 e0       	ldi	r22, 0x08	; 8
 100:	4a e0       	ldi	r20, 0x0A	; 10
 102:	50 e0       	ldi	r21, 0x00	; 0
 104:	0e 94 95 03 	call	0x72a	; 0x72a <_ZN5Print7printlnEhi>
 108:	1f 91       	pop	r17
 10a:	08 95       	ret

 

sadman41 пишет:

 Запись же в ячейку памяти не произвольного значения, а явно находящегося в невыполняемом блоке - уже за гранью добра и зла )) 

Ну Вы ж не инитили переменную, значить не возражали )))

Из кода видно - нету переменной, нету ячейки, только константы, только хард ;) В r17 параметр _nor и все.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

sadman41 пишет:

там всё нормально, в обоих строчках выводится значение переменной равное нулю.

и '0', и '8' - являются произвольными значениями.

если ожидается, что это значение определено, то оно явно не произвольно.

sadman41
Offline
Зарегистрирован: 19.10.2016

Logik пишет:

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

Ну Вы ж не инитили переменную, значить не возражали )))

Иду я по улице спокойно, потом раз - и очухиваюсь в КПЗ, без кошелька и в наручниках. Потом следствие выясняет, что я не высказал возражений местному гопнику Арсению по поводу того, чтобы мне били по голове сзади и тырили наличность, а так же, пока лежал, не возражал по поводу того, чтобы сотрудник 3-го отделения Л. насыпал мне в карманы патронов с целью выполнения плана по задержаниям особо опасных преступников. Простая оптимизация же - бегать ни за кем не надо, клиент лежит в луже. Со всех сторон логично ))

Вобщем, спасибо товарищи, механизм срабатывания этого "крекс-пекс-фекса" я понял - простое невыделение ячейки памяти, а не помещение в нее значений (как разумных, так и неразумных) и последующее их изменение по избранному компилятором алгоритму до момента явной инициализации.

 

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

sadman41 пишет:

Иду я по улице спокойно, потом раз - и очухиваюсь в КПЗ, без кошелька и в наручниках.

простое невыделение ячейки памяти, а не помещение в нее значений

параноидальная настойчивость превратить произвольное значение в определённое.

если бы так и было, то в описании произвольного значения было бы примечание про ячейки памяти или кошелёк.

sadman41
Offline
Зарегистрирован: 19.10.2016

Клапауций 112 пишет:
параноидальная настойчивость превратить произвольное значение в определённое

А после этой фразы еще и дислексия )) 

Сотру, пожалуй, IDE. Буду пускать слюну и строгать палочки.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:

механизм срабатывания этого "крекс-пекс-фекса" я понял - простое невыделение ячейки памяти, а не помещение в нее значений (как разумных, так и неразумных) и последующее их изменение по избранному компилятором алгоритму до момента явной инициализации.

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

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

Больше тут понимать нечего.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Брат! Если у тебя включен оптимизатор, то память не выделена в момент декларации. Так - по стандарту положено.

Следствие этого - разные значения неинициализированной переменной в разных местах кода. Считай, что это каждый раз - случайное число.

Если ты хочешь иметь, пусть случайное, но всегда одинаковое число, то память должна быть выделена. Значит отключи оптимизатор. Или используй статик (или глобал). Под них компилятор выделит память в момент декларации (скорее всего ;) ).

---------------

прочел пост Евгения. И так тоже возможно.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

sadman41 пишет:

А после этой фразы еще и дислексия )) 

Сотру, пожалуй, IDE. Буду пускать слюну и строгать палочки.

таки, это патология - ты подсознательно уверен, что тебя будут уговаривать использовать avr-gcc ?

*реакция форумчан не определена: от хоть убейся ап стену, до удачи в пешеходной экскурсии в счастье.

sadman41
Offline
Зарегистрирован: 19.10.2016

Товарищи, я понимаю, что по местной традиции в любой теме нужно устроить срач, потом спросить чей Крым и исписать еще страниц сто.

Я уже увидел, что в данном конкретном случае именно оптимизация (в целом) приводит к тому, что логическая структура, написанная на языке C++ не переводится в asm, как я этого ожидал. Спасибо всем помогавшим разобраться. 

Да, я не настоящий программист, а поддельный (всех обманул и пробрался в святая святых с корыстной целью) -  теперь Тува будет наконец-то полностью завалена обломками и залита керосином. На унитаз я сажусь не проверяя есть ли в бачке вода и в душ залезаю без проверки наличия шампуня во флаконе (ожидаю, что все на месте). Перед тем, как сажусь в машину, не делаю лабораторных исследований на предмет соответствия заданным параметрам сцепления шины с грунтовой поверхностью (ведь кто знает, как изменилась структура покрытия колеса за то время, пока я спал). Не знаю, сможете ли вы меня простить за это. Ну, или хотя бы понять.

И, Клапауций, - я просто тобой манипулирую чтобы ты стал меня уговаривать использовать avr-gcc. Это же очевидно.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

sadman41 пишет:

Иду я по улице спокойно, потом раз - и очухиваюсь в КПЗ, без кошелька и в наручниках. Потом следствие выясняет, что я не высказал возражений местному гопнику Арсению по поводу того, чтобы мне били по голове сзади и тырили наличность, а так же, пока лежал, не возражал по поводу того, чтобы сотрудник 3-го отделения Л. насыпал мне в карманы патронов с целью выполнения плана по задержаниям особо опасных преступников.

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

Надеюсь, аналогия понятна. 

 

PS. Но вообще пример, конечно, интересный.