Подтвердить что ты не робот

Присвоение значения во время цикла

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?

4b9b3361

Ответ 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.

Выполнено следующим образом:

  • Первая строка
    • положить 10 в a
  • Вторая строка
    • оцените 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;
}