Присвоение значения во время цикла
I нашел этот фрагмент кода в Википедии.
#include <stdio.h>
int main(void)
{
int c;
while (c = getchar(), c != EOF && c != 'x')
{
switch (c)
{
case '\n':
case '\r':
printf ("Newline\n");
break;
default:
printf ("%c",c);
}
}
return 0;
}
Мне любопытно выражение, используемое как условие для цикла while:
while (c = getchar(), c != EOF && c != 'x')
Совершенно очевидно, что он делает, но я никогда не видел эту конструкцию раньше. Является ли это конкретным для цикла while? Если нет, то как анализатор/компилятор определяет, какая часть выражения, разделенного запятыми, возвращает логическое значение для цикла while?
Ответы
Ответ 1
Оператор запятой является двоичным оператором, который оценивает свой первый операнд и отбрасывает результат, затем оценивает второй операнд и возвращает это значение.
Это также "sequence point" , что означает, что все побочные эффекты будут вычислены до того, как будет выполнена следующая часть кода.
Ответ 2
Оператор запятой - это странное зверь, пока вы не поймете его, и это не относится к while
.
Выражение:
exp1, exp2
оценивает exp1
, затем оценивает exp2
и возвращает exp2
.
Вы часто это видите, хотя можете не осознавать этого:
for (i = j = 0; i < 100; i++, j += 2)
Фактически вы не используете возвращаемое значение из "i++, j += 2"
, но оно тем не менее. Оператор запятой оценивает оба бита для изменения как i
, так и j
.
Вы можете очень хорошо использовать его в любом месте, где может использоваться нормальное выражение (эта запятая внутри вызовов вашей функции не является, например, оператором запятой), и это очень полезно при написании компактного исходного кода, если это вам нравится. Таким образом, это часть семьи, которая позволяет такие вещи, как:
while ((c= getchar()) != EOF) {...}
i = j = k = 0;
и т.д.
В вашем конкретном примере:
while (c = getchar(), c != EOF && c != 'x')
происходит следующее:
-
c = getchar()
выполняется полностью (оператор запятой является точкой последовательности).
Выполняется -
c != EOF && c != 'x'
.
- оператор запятой выбрасывает первое значение (c) и "возвращает" второе.
-
while
использует это возвращаемое значение для управления циклом.
Ответ 3
Во многих языках запятая - это оператор, который всегда приводит к значению второго операнда. Операнды последовательно оцениваются слева направо.
Псевдо-код:
a = 10
print a = 7 + 8, a * 2
Примечание: print
считается оператором, который не принимает аргументы, поэтому то, что приходит после, считается единственным выражением a = 7 + 8, a * 2
.
Выполнено следующим образом:
- Первая строка
- Вторая строка
- оцените
7 + 8
(15
)
- положите оценочное значение (
15
) в a
- оцените
a * 2
(30
)
- оцените оператор
,
с операндами 15
и 30
:
- всегда значение второго операнда (
30
)
- напечатать оцениваемое значение (
30
)
Ответ 4
Чтобы разбить бит на другие ответы, в этом коде:
EXPRESSION_1 , EXPRESSION_2
EXPRESSION_1 сначала оценивается, то есть точка последовательности, затем вычисляется EXPRESSION_2, а значение всего объекта - значение EXPRESSION_2.
Порядок гарантии операции и точка последовательности важны для кода, который вы указали. Вместе они означают, что мы можем быть уверены, что функция getchar() вызывается, а значение переменной c полностью обновляется до того, как будет проверено значение c.
Ответ 5
Запятая - это оператор. Он возвращает значение выражения правой руки по умолчанию. Порядок оценки гарантированно будет оставлен первым, а затем правильным.
UPDATE (ответ на комментарий Pax):
Как и большинство операторов, он может быть перегружен для пользовательских типов:
#include <iostream>
#include <string>
using namespace std;
enum EntryType { Home, Cell, Address };
class AddressBookEntryReference {
public:
AddressBookEntryReference(const string& name, const EntryType &entry)
: Name(name), Entry(entry) { }
string Name;
EntryType Entry;
};
AddressBookEntryReference operator,(const string& name, const EntryType &type) {
return AddressBookEntryReference(name, type);
}
class AddressBook {
string test;
public:
string& operator[](const AddressBookEntryReference item) {
// return something based on item.Name and item.Entry.
// just to test:
test = item.Name;
return test;
}
};
int main() {
// demo:
AddressBook book;
cout << book["Name", Cell] // cool syntax!
<< endl;
}