#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
следующем операторе при сравнении используется это значение со знаком. Без промежуточной переменной выражение не имеет знака, а сравнение является ложным.