Вот простой фрагмент C ++:
int x1 = 10, x2=20, y1=132, y2=12, minx, miny, maxx, maxy;
x1<=x2 ? minx=x1,maxx=x2 : minx=x2,maxx=x1;
y1<=y2 ? miny=y1,maxy=y2 : miny=y2,maxy=y1;
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
Я думал, что результат должен быть:
minx=10
maxx=20
miny=12
maxy=132
Но на самом деле результат таков:
minx=10
maxx=10
miny=12
maxy=132
Может ли кто-нибудь дать объяснение, почему maxx не 20? Благодарю.
Из-за приоритета оператора выражение разбирается так:
(x1<=x2 ? minx=x1,maxx=x2 : minx=x2), maxx=x1;
Вы можете решить это с помощью:
(x1<=x2) ? (minx=x1,maxx=x2) : (minx=x2, maxx=x1);
И на самом деле вам не нужны первые две пары скобок. Также проверьте этот вопрос.
Приоритет условного оператора больше, чем у оператора запятой, поэтому
x1<=x2 ? minx=x1,maxx=x2 : minx=x2,maxx=x1;
заключено в скобки как
(x1<=x2 ? minx=x1,maxx=x2 : minx=x2),maxx=x1;
Таким образом, последнее назначение выполняется независимо от условия.
Чтобы исправить это, вы можете
используйте скобки:
x1 <= x2 ? (minx = x1, maxx = x2) : (minx = x2, maxx = x1);
(ты не необходимость скобки в true
ветка, но имо лучше их тоже иметь).
использовать два условия:
minx = x1 <= x2 ? x1 : x2;
maxx = x1 <= x2 ? x2 : x1;
использовать if
:
if (x1 <= x2) {
minx = x1;
maxx = x2;
} else {
minx = x2;
maxx = x1;
}
Составленный с оптимизацией или без, if
Версия и условные скобки, заключенные в скобки с запятыми, создают одинаковую сборку как в gcc (4.7.2), так и в clang (3.2), поэтому можно ожидать, что и в других компиляторах. Версия с двумя условными выражениями дает различную сборку, но с оптимизацией оба этих компилятора испускают только один cmp
инструкция для этого тоже.
На мой взгляд, if
Версию проще всего проверить на правильность, поэтому предпочтительнее.
В то время как другие объяснили, в чем причина проблемы, я думаю, что «лучшим» решением должно быть написание условного выражения, если:
int x1 = 10, x2=20, y1=132, y2=12, minx, miny, maxx, maxy;
if (x1<=x2)
{
minx=x1;
maxx=x2;
}
else
{
minx=x2;
maxx=x1;
}
if (y1<=y2)
{
miny=y1;
maxy=y2;
}
else
{
miny=y2;
maxy=y1;
}
Да, это на несколько строк длиннее, но также легче читать и четко понимать, что происходит (и если вам нужно пройти через это в отладчике, вы можете легко увидеть, как это происходит).
Любой современный компилятор должен быть в состоянии преобразовать любой из них в довольно эффективные условные присваивания, которые хорошо справляются с задачей избегания ветвлений (и, следовательно, «плохого предсказания ветвлений»).
Я подготовил небольшой тест, который я скомпилировал, используя
g++ -O2 -fno-inline -S -Wall ifs.cpp
Вот источник (я должен был сделать это параметрами, чтобы компилятор не просто вычислял правильное значение напрямую, а просто делал mov $12,%rdx
, но на самом деле сделал сравнение и решил с большим):
void mine(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
if (x1<=x2)
{
minx=x1;
maxx=x2;
}
else
{
minx=x2;
maxx=x1;
}
if (y1<=y2)
{
miny=y1;
maxy=y2;
}
else
{
miny=y2;
maxy=y1;
}
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
void original(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
x1<=x2 ? (minx=x1,maxx=x2) : (minx=x2,maxx=x1);
y1<=y2 ? (miny=y1,maxy=y2) : (miny=y2,maxy=y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
void romano(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
minx = ((x1 <= x2) ? x1 : x2);
maxx = ((x1 <= x2) ? x2 : x1);
miny = ((y1 <= y2) ? y1 : y2);
maxy = ((y1 <= y2) ? y2 : y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
int main()
{
int x1=10, x2=20, y1=132, y2=12;
mine(x1, x2, y1, y2);
original(x1, x2, y1, y2);
romano(x1, x2, y1, y2);
return 0;
}
Сгенерированный код выглядит так:
_Z4mineiiii:
.LFB966:
.cfi_startproc
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %ecx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %esi, %r12d
subq $40, %rsp
movl %edi, %r13d
cmpl %esi, %edi
movl %edx, %ebp
cmovg %edi, %r12d
cmovg %esi, %r13d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovg %edx, %ebx
cmovg %ecx, %ebp
.... removed actual printout code that is quite long and unwieldy...
_Z8originaliiii:
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %ecx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %esi, %r12d
subq $40, %rsp
movl %edi, %r13d
cmpl %esi, %edi
movl %edx, %ebp
cmovg %edi, %r12d
cmovg %esi, %r13d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovg %edx, %ebx
cmovg %ecx, %ebp
... print code goes here ...
_Z6romanoiiii:
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %edx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %edi, %r12d
subq $40, %rsp
movl %esi, %r13d
cmpl %esi, %edi
movl %ecx, %ebp
cmovle %edi, %r13d
cmovle %esi, %r12d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovle %edx, %ebp
cmovle %ecx, %ebx
... printout code here....
Как вы видете, mine
а также original
идентичен, и romano
использует немного разные регистры и другую форму cmov
, но в противном случае они делают то же самое в том же количестве инструкций.
Интересный вопрос как о приоритетности операций, так и о генерации кода.
в порядке, ,
операция имеет ОЧЕНЬ низкий приоритет (самый низкий, см. справочная таблица). Из-за этого ваш код такой же, как в следующих строках:
((x1<=x2) ? minx=x1,maxx=x2 : minx=x2),maxx=x1;
((y1<=y2) ? miny=y1,maxy=y2 : miny=y2),maxy=y1;
На самом деле только грамматика C / C ++ предотвращает в первую очередь ,
от того же поведения.
Еще одно действительно опасное место в приоритетах операций C / C ++ — побитовые операции и сравнение. Подумайте о следующем фрагменте:
int a = 2;
int b = (a == 2|1); // Looks like check for expression result? Nope, results 1!
Заглядывая вперед, я бы порекомендовал переписать ваш фрагмент таким образом, сохраняя баланс между эффективностью и удобочитаемостью:
minx = ((x1 <= x2) ? x1 : x2);
maxx = ((x1 <= x2) ? x2 : x1);
miny = ((y1 <= y2) ? y1 : y2);
maxy = ((y1 <= y2) ? y2 : y1);
Наиболее интересным фактом об этом фрагменте кода является то, что такой стиль может привести к наиболее эффективному коду для некоторых архитектур, таких как ARM, из-за флагов битов условий в наборе команд процессора (дублирование условий ничего не стоит и больше, указывает компилятору на правильные фрагменты кода).
Из-за приоритета оператора:
(x1<=x2 ? minx=x1,maxx=x2 : minx=x2),maxx=x1
Вы можете исправить это с помощью:
int x1=10, x2=20, y1=132, y2=12, minx, miny, maxx, maxy;
x1<=x2 ? (minx=x1,maxx=x2) : (minx=x2,maxx=x1);
y1<=y2 ? (miny=y1,maxy=y2) : (miny=y2,maxy=y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
В C ++ 11 вы можете использовать std::tie
а также std::make_pair
чтобы сделать это очевидно правильным с первого взгляда (TM)
#include <tuple>
#include <utility>
#include <iostream>
using namespace std;
int main()
{
int x1 = 10, x2=20, y1=132, y2=12, minx, miny, maxx, maxy;
tie(minx, maxx) = (x1 <= x2)? make_pair(x1, x2) : make_pair(x2, x1);
tie(miny, maxy) = (y1 <= y2)? make_pair(y1, y2) : make_pair(y2, y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
онлайн выход.
Это семантически эквивалентно всем другим опубликованным решениям, и с любым приличным оптимизирующим компилятором не имеет никаких накладных расходов. Это синтаксически намного приятнее, потому что
В качестве небольшого изменения, которое обобщает поиск указателей на минимальный и максимальный элементы последовательностей, вы можете использовать std::minmax_element
и тот факт, что необработанные массивы имеют функции, не являющиеся членами begin()
а также end()
(обе функции C ++ 11)
#include <algorithm>
#include <tuple>
#include <iostream>
using namespace std;
int main()
{
int x[] = { 10, 20 }, y[] = { 132, 12 }, *minx, *miny, *maxx, *maxy;
tie(minx, maxx) = minmax_element(begin(x), end(x));
tie(miny, maxy) = minmax_element(begin(y), end(y));
cout<<"minx="<<*minx<<"\n";
cout<<"maxx="<<*maxx<<"\n";
cout<<"miny="<<*miny<<"\n";
cout<<"maxy="<<*maxy<<"\n";
}
онлайн выход.