Метод seq
является самым простым, но у Bash есть встроенная арифметическая оценка.
END=5
for ((i=1;i<=END;i++)); do
echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines
Конструкция for ((expr1;expr2;expr3));
работает так же, как for (expr1;expr2;expr3)
на C и подобных языках, и нравится других ((expr))
случаях, Bash рассматривает их как арифметику.
if (Derived* derived = dynamic_cast<Derived*>(base)) {
// do stuff with `derived`
}
Хотя это часто упоминается как антишаблон («использовать виртуальную рассылку!»), Иногда тип Derived
обладает функциональностью, которой просто не обладает Base
(и, следовательно, отдельными функциями), и это хороший способ включить эту семантическую разницу.
Оператор присваивания возвращает значение назначенного значения . Итак, я мог бы использовать его в такой ситуации:
if(x = getMyNumber())
Затем я присваиваю x
значение, возвращаемое getMyNumber
, и проверяю, не равен ли он нулю.
Избегайте этого, я привел вам пример, чтобы помочь вам понять это.
Редактировать: добавление Просто предложение (может понравиться).
Чтобы избежать таких ошибок, вплоть до некоторых расширений, нужно записать условие if как if(NULL == ptr)
вместо if(ptr == NULL)
, потому что при неправильном написании оператора проверки равенства ==
в качестве оператора =
, компиляция выдаст ошибку lvalue с if(NULL = ptr)
, но if(res = NULL)
, переданную компилятором (что вы не имеете в виду), и останется ошибкой в коде для среды выполнения.
По той же причине я предпочитаю писать if(getMyNumber() ==x)
вместо if(x == getMyNumber())
Следует также читать: Критика в отношении этого вида кода.
Я столкнулся с случаем, когда это было полезно совсем недавно, поэтому я решил опубликовать его.
Предположим, вы хотите проверить несколько условий в одном if, и если какое-либо из условий выполняется, вы хотите сгенерировать сообщение об ошибке. Если вы хотите включить в свое сообщение об ошибке, какое конкретное условие вызвало ошибку, вы можете сделать следующее:
std::string e;
if( myMap[e = "ab"].isNotValid() ||
myMap[e = "cd"].isNotValid() ||
myMap[e = "ef"].isNotValid() )
{
// here, e has the key for which the validation failed
}
Так что, если второе условие является тем, которое оценивается как true, e будет равно " CD". Это связано с поведением короткого замыкания в ||
, которое предписано стандартом (если не перегружено). См. этот ответ для более подробной информации о коротком замыкании.
Это зависит от того, хотите ли вы писать чистый код или нет. Когда C впервые разрабатывался, важность чистого кода не была полностью осознана, и компиляторы были очень просты: использование подобного вложенного назначения часто могло привести к более быстрому коду. Сегодня я не могу вспомнить ни одного случая, когда хороший программист сделал бы это. Это просто делает код менее читаемым и более сложным в обслуживании.
почему компилятор не выдает предупреждение
Некоторые компиляторы будут генерировать предупреждения для подозрительных назначений в условном выражении, хотя обычно вам нужно включить предупреждение явно.
Например, в Visual C ++ необходимо включить C4706 (или предупреждения уровня 4 в целом). Я обычно включаю как можно больше предупреждений и делаю код более явным, чтобы избежать ложных срабатываний. Например, если бы я действительно хотел сделать это:
if (x = Foo()) { ... }
Тогда я бы написал это как:
if ((x = Foo()) != 0) { ... }
Компилятор видит явный тест и предполагает, что назначение было преднамеренным так что вы не получите здесь ложного положительного предупреждения.
Единственный недостаток этого подхода - то, что вы не можете использовать его, когда переменная объявлена в условии. То есть вы не можете переписать:
if (int x = Foo()) { ... }
как
if ((int x = Foo()) != 0) { ... }
Синтаксически, это не работает. Таким образом, вы должны либо отключить предупреждение, либо пойти на компромисс в отношении того, насколько плотно вы работаете x
.
ОБНОВЛЕНИЕ: В C ++ 17 добавлена возможность иметь оператор init в условии для оператора if ( p0305r1 ), что хорошо решает эту проблему (для вида сравнения, а не только != 0
).
if (x = Foo(); x != 0) { ... }
Кроме того, если вы хотите, вы можете ограничить область действия x
только выражением if:
if (int x = Foo(); x != 0) { /* x in scope */ ... }
// x out of scope
Вот немного истории о синтаксисе, о котором идет речь.
В классическом C обработка ошибок часто выполнялась путем написания чего-то вроде:
int error;
...
if(error = foo()) {
printf("An error occured: %s\nBailing out.\n", strerror(error));
abort();
}
Или, когда был вызов функции, который мог бы вернуть нулевой указатель, идиома использовалась наоборот:
Bar* myBar;
... //in old C variables had to be declared at the start of the scope
if(myBar = getBar()) {
//do something with myBar
}
Однако этот синтаксис опасно близок к
if(myValue == bar()) ...
, поэтому многие люди считают присвоение внутри условия плохим стилем, и компиляторы начали предупреждать об этом (по крайней мере, с -Wall
). Однако этого предупреждения можно избежать, добавив дополнительный набор скобок:
if((myBar = getBar())) { //tells the compiler: Yes, I really want to do that assignment!
Затем появился C99, позволяющий смешивать определения и утверждения, поэтому многие разработчики часто пишут что-то вроде
Bar* myBar = getBar();
if(myBar) {
, который чувствует себя неловко. Вот почему новейший стандарт разрешает определения внутри условий, чтобы обеспечить краткий, элегантный способ сделать это:
if(Bar* myBar = getBar()) {
В этом утверждении больше нет опасности, вы явно задаете переменную тип, явно желая это должно быть инициализировано. Это также позволяет избежать лишней строки для определения переменной, что приятно. Но самое главное, теперь компилятор может легко перехватить ошибку такого рода:
if(Bar* myBar = getBar()) {
...
}
foo(myBar->baz); //compiler error
//or, for the C++ enthusiasts:
myBar->foo(); //compiler error
Без определения переменной внутри оператора if
это условие не будет обнаружено.
Короче говоря, длинный ответ: синтаксис в вашем вопросе является продуктом простоты и мощи старого Си, но это зло, поэтому компиляторы могут предупредить об этом. Поскольку это также очень полезный способ выражения общей проблемы, теперь существует очень лаконичный и надежный способ добиться того же поведения. И есть много хороших, возможных применений для этого.
Выполнение задания в if
является довольно распространенной вещью, хотя также часто люди делают это случайно.
Обычный шаблон:
if (int x = expensive_function_call())
{
// ...do things with x
}
Анти-шаблон - это то, где вы ошибочно назначаете вещи:
if (x = 1)
{
// Always true
}
else
{
// Never happens
}
Вы можете избежать этого до некоторой степени поставив сначала свои константы или значения const
, ваш компилятор выдаст ошибку:
if (1 = x)
{
// Compiler error, can't assign to 1
}
=
против ==
- это то, что вам нужно для развития. Я обычно ставлю пробел вокруг оператора, чтобы было более очевидно, какая операция выполняется, поскольку longname=longername
выглядит очень похоже на longname==longername
, но сами по себе =
и ==
, очевидно, различаются.