Перейти к содержанию
    

Недавно столкнулся с одной странностью (по крайней мере для меня). Хотелось бы понять.

Есть фрагмент года (упрощенный до безобразия):

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.

И код работает как надо только в таком варианте:

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если так:

int16u temp1, temp2;
    for (int32u i = 0; i < 10; i++)
    {
        temp1 = 0x0001;
        temp2 = 0xFFFE;
        if ( (int16u)(~temp1) != temp2 ) continue;
        temp2 = temp1;
    }

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если так:

int16u temp1, temp2;
    for (int32u i = 0; i < 10; i++)
    {
        temp1 = 0x0001;
        temp2 = 0xFFFE;
        if ( (int16u)(~temp1) != temp2 ) continue;
        temp2 = temp1;
    }

Так как Вы предлагаете, тоже работает. Хотя в этом меня тоже смущает один момент. После выполнения оператора "~" не должна меняться размерность переменной.

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

И при этом никаких предупреждений от компилятора.

Как это можно рассматривать? Бага компилятора? Или есть более простое объяснение?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

После выполнения оператора "~" не должна меняться размерность переменной.
Может меняться и после и до. В данном случае меняется до. Читайте про неявные приведения типов (implicit type conversion) и правила расширения целых (integer promotion rules).

Как это можно рассматривать? Бага компилятора? Или есть более простое объяснение?
Как обычно (в 99.99% случев) - компилятор не виноват, просто программист не удосужился изучить язык, который пытается использовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может меняться и после и до. В данном случае меняется до. Читайте про неявные приведения типов (implicit type conversion) и правила расширения целых (integer promotion rules).

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

Про неявное приведение типов соглашусь.

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

Так сообщите пожалуйста. Мне это по прежнему непонятно. Речь идёт о самом первом примере.

Поясняю ещё раз. Дело не в том, что результат выполнения условной операции неверный. Дело в том, что условный оператор не выполняется вообще.

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

   \   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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Так сообщите пожалуйста. Мне это по прежнему непонятно. Речь идёт о самом первом примере.

Да, знаю. После выполнения расширения типов ваше условие выполняется всегда, для любых значений переменных temp1 и temp2 (0xFFFFxxxx всегда не равно 0x0000xxxx). Поэтому условие всегда истинно и проверять его не имеет смысла - вы же хотите, чтобы ваша программа была маленькой и быстрой, поэтому компилятор просто выкинул бессмысленную проверку и весь код, который никогда, ни при каких условиях выполняться не будет.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, знаю. После выполнения расширения типов ваше условие выполняется всегда, для любых значений переменных temp1 и temp2 (0xFFFFxxxx всегда не равно 0x0000xxxx). Поэтому условие всегда истинно и проверять его не имеет смысла - вы же хотите, чтобы ваша программа была маленькой и быстрой, поэтому компилятор просто выкинул бессмысленную проверку и весь код, который никогда, ни при каких условиях выполняться не будет.

Позволю себе усомниться по двум пунктам:

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

2. Я уже писал, что если переменные объявить как int32u, то условие тоже всегда истинно. Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него.

Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях.

Кстати уровень оптимизации = Null, поэтому с этой стороны не следует ожидать от компилятора излишней предупредительности.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

Это вам показалось. Если припомните, какое именно было предупреждение, то поймёте, что в данном случае оно не применимо. А если не припомните, то это просто испорченный телефон. Короче, показалось.

 

Но в этом случае компилятор не выбрасывает из кода условный оператор и всё что после него.

Есть вероятность, что ответ на этот простой вопрос, как обычно, лежит в более сложных материях.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

Например, если в том же примере заменить строку с условным оператором "if (true) continue;" то получаем предупреждение:

Warning[Pe111]: statement is unreachable - что логично. А в моем примере это происходило "молча".

 

Тем не менее всем спасибо!

Ошибка локализована. И с утверждением, что компилятор "не обязан", я согласен. Остальное в общем то уже нюансы функционирования программы, которой также является и компилятор.

Изменено пользователем amiller

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

...

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

... И с утверждением, что компилятор "не обязан", я согласен...

Почему? Ведь мог и написать что-то. Но нет промолчал как рыба.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В этом с Вами я согласен, но предупредить то надо.

Зря соглашаетесь. Хороший компилятор (и некий стандарт предупреждений) должен уметь определять опечатки и вероятные ошибки. Согласие в данном случае с тем, что он не обязан.

 

Обнаружил как-то у себя код

{
bla bla bla;
bla bla bla;
} while (++i<10);

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

 

----

 

Но "стандарт предупреждений" неудачное название. История показывает, что каждый (американский) стандартер желает наВрать, где сидит фазан. Лучше "правила" с приоритетами.

Опечатку выделил. Опечатался буквой си.

Изменено пользователем GetSmart

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Обнаружил как-то у себя код ...

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Обнаружил как-то у себя код

{
bla bla bla;
bla bla bla;
} while (++i<10);

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

Ой ли?

{
  bla bla bla;
  bla bla bla;
}
while (++i<10);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

хороший - обязан (выдать предупреждение), сишный - не обязан

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

{
bla bla bla;
bla bla bla;
} while (++i<10);

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

И что в этом коде неладно???

Следуя Вашей логике, компилятор на выражение:

int x;

тоже должен выдавать варнинг, ведь может Вы там хотели написать:

unsigned int x;

Компилятор не должен думать за программиста.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

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

×
×
  • Создать...