реклама на сайте
подробности

 
 
13 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> Детская ошибка
amiller
сообщение May 2 2016, 06:22
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
AleksBak
сообщение May 2 2016, 07:08
Сообщение #2


Частый гость
**

Группа: Участник
Сообщений: 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;
    }
Go to the top of the page
 
+Quote Post
amiller
сообщение May 2 2016, 07:16
Сообщение #3


Частый гость
**

Группа: Участник
Сообщений: 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;
    }

Так как Вы предлагаете, тоже работает. Хотя в этом меня тоже смущает один момент. После выполнения оператора "~" не должна меняться размерность переменной.
Но больше всего меня беспокоит результат компиляции первого моего примера. В ассемблерном коде вообще отсутствует условный оператор и всё, что после него.
И при этом никаких предупреждений от компилятора.
Как это можно рассматривать? Бага компилятора? Или есть более простое объяснение?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение May 2 2016, 07:24
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
amiller
сообщение May 2 2016, 07:36
Сообщение #5


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение May 2 2016, 08:09
Сообщение #6


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
amiller
сообщение May 2 2016, 08:21
Сообщение #7


Частый гость
**

Группа: Участник
Сообщений: 176
Регистрация: 20-02-14
Из: Томск
Пользователь №: 80 612



Цитата(Сергей Борщ @ May 2 2016, 11:09) *
Да, знаю. После выполнения расширения типов ваше условие выполняется всегда, для любых значений переменных temp1 и temp2 (0xFFFFxxxx всегда не равно 0x0000xxxx). Поэтому условие всегда истинно и проверять его не имеет смысла - вы же хотите, чтобы ваша программа была маленькой и быстрой, поэтому компилятор просто выкинул бессмысленную проверку и весь код, который никогда, ни при каких условиях выполняться не будет.

Позволю себе усомниться по двум пунктам:
1. Раньше во всех случаях, когда в коде был фрагмент, который никогда не выполняется, я получал от компилятора соответствующее предупреждение. В данном случае - нет.
2. Я уже писал, что если переменные объявить как int32u, то условие тоже всегда истинно. Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него.
Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях.
Кстати уровень оптимизации = Null, поэтому с этой стороны не следует ожидать от компилятора излишней предупредительности.
Go to the top of the page
 
+Quote Post
scifi
сообщение May 2 2016, 11:30
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 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) *
Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него.
Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях.

Не надо там искать сложных материй. В одном случае выкинул бессмысленный код, в другом - нет. Подумаешь - бывает сплошь и рядом. Компилятор - это просто программа, а не какое-то высшее существо с высшим разумом.
Go to the top of the page
 
+Quote Post
amiller
сообщение May 2 2016, 11:35
Сообщение #9


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
AleksBak
сообщение May 2 2016, 12:20
Сообщение #10


Частый гость
**

Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364



Цитата(amiller @ May 2 2016, 15:35) *
В этом с Вами я согласен, но предупредить то надо. Например если поставить внутри функции безусловный return, генерится предупреждение. А в данном случае всё происходит молча. Ну и не совсем понятен алгоритм. В одном случае молча выбрасывает код, а в другом очень похожем случае всё же генерит код, который тем не менее также не выполняется.
...

Я было подумал, что IAR-овский компилер тут и поэтому может так, а оказывается и gcc аналогично себя ведет в этой ситуации.
Цитата(amiller @ May 2 2016, 15:35) *
... И с утверждением, что компилятор "не обязан", я согласен...

Почему? Ведь мог и написать что-то. Но нет промолчал как рыба.
Go to the top of the page
 
+Quote Post
GetSmart
сообщение May 2 2016, 13:49
Сообщение #11


.
******

Группа: Участник
Сообщений: 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


--------------------
Заблуждаться - Ваше законное право :-)
Go to the top of the page
 
+Quote Post
Baser
сообщение May 2 2016, 16:18
Сообщение #12


Просто Che
*****

Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881



Цитата(GetSmart @ May 2 2016, 16:49) *
Обнаружил как-то у себя код ...
В котором в первой строке перед скобкой пропущен do. Просматривая такой код, ошибка не особо и видна. А компилятору заметить здесь что-то неладное совсем не сложно. Абыдно.

Пример вы выбрали для подтверждения своей мысли сильно неудачный. Ибо и без do и с do конструкция совершенно легальна, хотя и имеет различную логику работы. А телепатическими способностями, чтобы понять то, что вы не написали, компиляторы уж точно не обладают sm.gif
Go to the top of the page
 
+Quote Post
ViKo
сообщение May 2 2016, 16:19
Сообщение #13


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 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);
Go to the top of the page
 
+Quote Post
Огурцов
сообщение May 2 2016, 16:36
Сообщение #14


Гуру
******

Группа: Участник
Сообщений: 3 928
Регистрация: 28-03-07
Из: РФ
Пользователь №: 26 588



Цитата(GetSmart @ May 2 2016, 13:49) *
Хороший компилятор (и некий стандарт предупреждений) должен уметь определять опечатки и вероятные ошибки. Согласие в данном случае с тем, что он не обязан.

хороший - обязан (выдать предупреждение), сишный - не обязан
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 3 2016, 15:33
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 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;
Компилятор не должен думать за программиста.
Go to the top of the page
 
+Quote Post

13 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 16:34
Рейтинг@Mail.ru


Страница сгенерированна за 0.01523 секунд с 7
ELECTRONIX ©2004-2016