|
Детская ошибка |
|
|
|
May 2 2016, 06:22
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612

|
Недавно столкнулся с одной странностью (по крайней мере для меня). Хотелось бы понять. Есть фрагмент года (упрощенный до безобразия): CODE int16u temp1, temp2; for (int32u i = 0; i < 10; i++) { temp1 = 0x0001; temp2 = 0xFFFE; if (~temp1 != temp2) continue; temp2 = temp1; }
Так вот, в этом фрагменте условный оператор и код после него никогда не выполняется. В отладчике после выполнения операции "temp2 = 0xFFFE;" происходит сразу переход на начало цикла. Переменные объявлены как int16u (== unsigned short (16 бит беззнаковое)) При этом никаких предупреждений на этапе компиляции. Если переменные объявить как int32u (== unsigned long (32 бита беззнаковое)), то условный оператор выполняется, но естественно результат всегда true. И код работает как надо только в таком варианте: CODE int16u temp1, temp2; for (int32u i = 0; i < 10; i++) { temp1 = 0x0001; temp2 = 0xFFFE; temp1 = ~temp1; if (temp1 != temp2) continue; temp2 = temp1; }
В этом случае выполняются все операторы, входящие в тело цикла. IAR C/C++ Compiler for ARM 7.40.3.8902 (7.40.3.8902) Добавлю: Оптимизация отключена. Проект под микроконтроллер STM32F105VCT6
Сообщение отредактировал amiller - May 2 2016, 06:41
|
|
|
|
|
May 2 2016, 07:08
|
Частый гость
 
Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364

|
А если так: Код int16u temp1, temp2; for (int32u i = 0; i < 10; i++) { temp1 = 0x0001; temp2 = 0xFFFE; if ( (int16u)(~temp1) != temp2 ) continue; temp2 = temp1; }
|
|
|
|
|
May 2 2016, 07:16
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612

|
Цитата(AleksBak @ May 2 2016, 10:08)  А если так: Код int16u temp1, temp2; for (int32u i = 0; i < 10; i++) { temp1 = 0x0001; temp2 = 0xFFFE; if ( (int16u)(~temp1) != temp2 ) continue; temp2 = temp1; } Так как Вы предлагаете, тоже работает. Хотя в этом меня тоже смущает один момент. После выполнения оператора "~" не должна меняться размерность переменной. Но больше всего меня беспокоит результат компиляции первого моего примера. В ассемблерном коде вообще отсутствует условный оператор и всё, что после него. И при этом никаких предупреждений от компилятора. Как это можно рассматривать? Бага компилятора? Или есть более простое объяснение?
|
|
|
|
|
May 2 2016, 07:24
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (amiller @ May 2 2016, 10:16)  После выполнения оператора "~" не должна меняться размерность переменной. Может меняться и после и до. В данном случае меняется до. Читайте про неявные приведения типов (implicit type conversion) и правила расширения целых (integer promotion rules). QUOTE (amiller @ May 2 2016, 10:16)  Как это можно рассматривать? Бага компилятора? Или есть более простое объяснение? Как обычно (в 99.99% случев) - компилятор не виноват, просто программист не удосужился изучить язык, который пытается использовать.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 2 2016, 07:36
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612

|
Цитата(Сергей Борщ @ May 2 2016, 10:24)  Может меняться и после и до. В данном случае меняется до. Читайте про неявные приведения типов (implicit type conversion) и правила расширения целых (integer promotion rules). Как обычно - компилятор не виноват, просто программист не удосужился изучить язык, который пытается использовать. Про неявное приведение типов соглашусь. Ну а судя по второй фразе, Вы точно знаете причину по которой компилятор удаляет часть кода, и при этом нет ни одного предупреждения? Так сообщите пожалуйста. Мне это по прежнему непонятно. Речь идёт о самом первом примере. Поясняю ещё раз. Дело не в том, что результат выполнения условной операции неверный. Дело в том, что условный оператор не выполняется вообще. В исходной программе после этого условного оператора у меня было под сотню строк кода до окончания цикла. И весь этот код отсутствует в ассемблере. CODE \ 00000000 0xB538 PUSH {R3-R5,LR} 68 int16u temp1, temp2; 69 70 for (int32u i = 0; i < 10; i++) \ 00000002 0x2000 MOVS R0,#+0 \ ??main_0: (+1) \ 00000004 0x280A CMP R0,#+10 \ 00000006 0xD206 BCS.N ??main_1 71 { 72 temp1 = 0x0001; \ 00000008 0x2101 MOVS R1,#+1 \ 0000000A 0x000C MOVS R4,R1 73 temp2 = 0xFFFE; \ 0000000C 0xF64F 0x71FE MOVW R1,#+65534 \ 00000010 0x000D MOVS R5,R1 74 if (~temp1 != temp2) continue; 75 temp2 = temp1; 76 } \ 00000012 0x1C40 ADDS R0,R0,#+1 \ 00000014 0xE7F6 B.N ??main_0 77 78 79 80
Сообщение отредактировал amiller - May 2 2016, 07:44
|
|
|
|
|
May 2 2016, 08:09
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (amiller @ May 2 2016, 10:36)  Ну а судя по второй фразе, Вы точно знаете причину по которой компилятор удаляет часть кода, и при этом нет ни одного предупреждения? Так сообщите пожалуйста. Мне это по прежнему непонятно. Речь идёт о самом первом примере. Да, знаю. После выполнения расширения типов ваше условие выполняется всегда, для любых значений переменных temp1 и temp2 (0xFFFFxxxx всегда не равно 0x0000xxxx). Поэтому условие всегда истинно и проверять его не имеет смысла - вы же хотите, чтобы ваша программа была маленькой и быстрой, поэтому компилятор просто выкинул бессмысленную проверку и весь код, который никогда, ни при каких условиях выполняться не будет.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 2 2016, 08:21
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612

|
Цитата(Сергей Борщ @ May 2 2016, 11:09)  Да, знаю. После выполнения расширения типов ваше условие выполняется всегда, для любых значений переменных temp1 и temp2 (0xFFFFxxxx всегда не равно 0x0000xxxx). Поэтому условие всегда истинно и проверять его не имеет смысла - вы же хотите, чтобы ваша программа была маленькой и быстрой, поэтому компилятор просто выкинул бессмысленную проверку и весь код, который никогда, ни при каких условиях выполняться не будет. Позволю себе усомниться по двум пунктам: 1. Раньше во всех случаях, когда в коде был фрагмент, который никогда не выполняется, я получал от компилятора соответствующее предупреждение. В данном случае - нет. 2. Я уже писал, что если переменные объявить как int32u, то условие тоже всегда истинно. Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него. Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях. Кстати уровень оптимизации = Null, поэтому с этой стороны не следует ожидать от компилятора излишней предупредительности.
|
|
|
|
|
May 2 2016, 11:30
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(amiller @ May 2 2016, 10:16)  Но больше всего меня беспокоит результат компиляции первого моего примера. В ассемблерном коде вообще отсутствует условный оператор и всё, что после него. Вы будете смеяться, но компилятор вовсе не обязан на каждый чих в исходнике генерить код. Обычно на минимальном уровне оптимизации генерит, но тоже не всегда и не везде. Компилятор отвечает только за то, что сгенерированный код выдаст тот результат, который следует из исходника. Если исходник никакого результата не подразумевает, то компилятор может не генерировать ничего - и будет в своём праве. Цитата(amiller @ May 2 2016, 11:21)  1. Раньше во всех случаях, когда в коде был фрагмент, который никогда не выполняется, я получал от компилятора соответствующее предупреждение. В данном случае - нет. Это вам показалось. Если припомните, какое именно было предупреждение, то поймёте, что в данном случае оно не применимо. А если не припомните, то это просто испорченный телефон. Короче, показалось. Цитата(amiller @ May 2 2016, 11:21)  Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него. Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях. Не надо там искать сложных материй. В одном случае выкинул бессмысленный код, в другом - нет. Подумаешь - бывает сплошь и рядом. Компилятор - это просто программа, а не какое-то высшее существо с высшим разумом.
|
|
|
|
|
May 2 2016, 11:35
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612

|
Цитата(scifi @ May 2 2016, 14:26)  Вы будете смеяться, но компилятор вовсе не обязан на каждый чих в исходнике генерить код. Обычно на минимальном уровне оптимизации генерит, но тоже не всегда и не везде. Компилятор отвечает только за то, что сгенерированный код выдаст тот результат, который следует из исходника. Если исходник никакого результата не подразумевает, то компилятор может не генерировать ничего - и будет в своём праве. В этом с Вами я согласен, но предупредить то надо. Например если поставить внутри функции безусловный return, генерится предупреждение. А в данном случае всё происходит молча. Ну и не совсем понятен алгоритм. В одном случае молча выбрасывает код, а в другом очень похожем случае всё же генерит код, который тем не менее также не выполняется. Например, если в том же примере заменить строку с условным оператором "if (true) continue;" то получаем предупреждение: Warning[Pe111]: statement is unreachable - что логично. А в моем примере это происходило "молча". Тем не менее всем спасибо! Ошибка локализована. И с утверждением, что компилятор "не обязан", я согласен. Остальное в общем то уже нюансы функционирования программы, которой также является и компилятор.
Сообщение отредактировал amiller - May 2 2016, 11:44
|
|
|
|
|
May 2 2016, 12:20
|
Частый гость
 
Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364

|
Цитата(amiller @ May 2 2016, 15:35)  В этом с Вами я согласен, но предупредить то надо. Например если поставить внутри функции безусловный return, генерится предупреждение. А в данном случае всё происходит молча. Ну и не совсем понятен алгоритм. В одном случае молча выбрасывает код, а в другом очень похожем случае всё же генерит код, который тем не менее также не выполняется. ... Я было подумал, что IAR-овский компилер тут и поэтому может так, а оказывается и gcc аналогично себя ведет в этой ситуации. Цитата(amiller @ May 2 2016, 15:35)  ... И с утверждением, что компилятор "не обязан", я согласен... Почему? Ведь мог и написать что-то. Но нет промолчал как рыба.
|
|
|
|
|
May 2 2016, 13:49
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(amiller @ May 2 2016, 15:35)  В этом с Вами я согласен, но предупредить то надо. Зря соглашаетесь. Хороший компилятор (и некий стандарт предупреждений) должен уметь определять опечатки и вероятные ошибки. Согласие в данном случае с тем, что он не обязан. Обнаружил как-то у себя код Код { bla bla bla; bla bla bla; } while (++i<10); В котором в первой строке перед скобкой пропущен do. Просматривая такой код, ошибка не особо и видна. А компилятору заметить здесь что-то неладное совсем не сложно. Абыдно. ---- Но "стандарт предупреждений" неудачное название. История показывает, что каждый (американский) стандартер желает наВрать, где сидит фазан. Лучше "правила" с приоритетами. Опечатку выделил. Опечатался буквой си.
Сообщение отредактировал GetSmart - May 2 2016, 14:10
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
May 2 2016, 16:19
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(GetSmart @ May 2 2016, 16:49)  Обнаружил как-то у себя код Код { bla bla bla; bla bla bla; } while (++i<10); В котором в первой строке перед скобкой пропущен do. Просматривая такой код, ошибка не особо и видна. А компилятору заметить здесь что-то неладное совсем не сложно. Абыдно. Ой ли? Код { bla bla bla; bla bla bla; } while (++i<10);
|
|
|
|
|
May 3 2016, 15:33
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(GetSmart @ May 2 2016, 19:49)  Код { bla bla bla; bla bla bla; } while (++i<10); В котором в первой строке перед скобкой пропущен do. Просматривая такой код, ошибка не особо и видна. А компилятору заметить здесь что-то неладное совсем не сложно. Абыдно. И что в этом коде неладно??? Следуя Вашей логике, компилятор на выражение: int x; тоже должен выдавать варнинг, ведь может Вы там хотели написать: unsigned int x; Компилятор не должен думать за программиста.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|