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

#c #if-statement #assembly #types #type-conversion

Вопрос:

При выполнении задачи с кодом leet я столкнулся со странной проблемой с векторами в C : оператор if напрямую переходил к else, даже если вычисленное выражение было оценено как истинное. Используя отладчик VS, я убедился, что это выражение верно, и действительно не понимал, почему оно так работает. После некоторых тестов я нашел способ заставить его работать : вычисляя ОДНО и то же выражение прямо перед оператором if и сохраняя его в другой переменной, оператор if работал должным образом. Я разобрал свой код с помощью отладчика VS и обнаружил, что этот конкретный оператор if вообще не оценивался : в конце оценки у меня есть инструкция jmp для части else. Сравнивая с двумя другими аналогичными утверждениями if, я заметил, что у них обоих есть «je» или «jns» вместо этого простого «jmp». Поскольку я не знаю, какой компилятор использует leetcode, я не могу сказать, относится ли он конкретно к компилятору Visual C .
Кто-нибудь знает, почему это так работает ? Я хотел бы лучше понять, что происходит и в чем заключается моя загадка, даже если бы я разгадал код. Мое понимание сборки и компиляторов на данный момент недостаточно хорошо, чтобы разобраться в этом самому.

Вот простой код, сравнивающий три похожих оператора if. Второй вариант не работает.

 #include lt;iostreamgt; #include lt;vectorgt;  int main() {  std::vectorlt;intgt;digits = { 9,9,9 };  int i = 3;  if (-1 lt; 0) {  int k = 0;   }   if ((digits.size() - 1 - i) lt; 0) {  int k = 0;  }  else {  int j = 0;  }   int test = digits.size() - 1 - i;  if (test lt; 0) {  int k = 0;  }  else {  int j = 0;  }  return 0; }  

Вот интересные детали в разборке; «безусловный jmp», о котором я говорил, — это инструкция 00B83E03.

 int i = 3; 00B83DDE mov dword ptr [i],3   if (-1 lt; 0) { 00B83DE5 mov eax,1  00B83DEA test eax,eax  00B83DEC je main 0A5h (0B83DF5h)   int k = 0;  00B83DEE mov dword ptr [ebp-30h],0   }   if ((digits.size() - 1 - i) lt; 0) { 00B83DF5 lea ecx,[digits]  00B83DF8 call std::vectorlt;int,std::allocatorlt;intgt; gt;::size (0B81190h)  00B83DFD sub eax,1  00B83E00 sub eax,dword ptr [i]  00B83E03 jmp main 0BEh (0B83E0Eh)   int k = 0; 00B83E05 mov dword ptr [ebp-3Ch],0   } 00B83E0C jmp main 0C5h (0B83E15h)   else {  int j = 0; 00B83E0E mov dword ptr [ebp-48h],0   }   int test = digits.size() - 1 - i; 00B83E15 lea ecx,[digits]  00B83E18 call std::vectorlt;int,std::allocatorlt;intgt; gt;::size (0B81190h)  00B83E1D sub eax,1  00B83E20 sub eax,dword ptr [i]  00B83E23 mov dword ptr [test],eax   if (test lt; 0) { 00B83E26 jns main 0E1h (0B83E31h)   int k = 0; 00B83E28 mov dword ptr [ebp-60h],0   } 00B83E2F jmp main 0E8h (0B83E38h)   else {  int j = 0; 00B83E31 mov dword ptr [ebp-6Ch],0   }  

Заранее благодарю вас !

Комментарии:

1. digits.size() возвращает значение без знака, поэтому оно (и любые выражения, в которых оно находится) никогда не будет меньше нуля.

2. Но отладчик говорит мне, что все выражение (digits.size()-i-1 Почему компилятор и отладчик не согласны друг с другом ? Это связано с какой-то оптимизацией компилятора или чем-то еще ?

3. Невозможно воспроизвести в MSVS 2022 . Обновите свой компилятор.

4. @xbeckers — Re: «даже если где-то есть слепок» — здесь нет слепков. Существуют неявные преобразования. Приведение-это то, что вы пишете в своем исходном коде, чтобы указать компилятору выполнить преобразование.

5. При построении test беззнаковое значение из size выражения неявно преобразуется в знаковый тип. В if следующем операторе при сравнении используется это значение со знаком. Без промежуточной переменной выражение не имеет знака, а сравнение является ложным.