Присвоение значения условию цикла while - PullRequest
13 голосов
/ 16 июля 2009

Я нашел этот кусок кода в Википедии.

#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')

Совершенно очевидно, что он делает, но я никогда раньше не видел эту конструкцию. Это специфично для в то время как цикл? Если нет, то как анализатор / компилятор определяет, какая сторона выражения, разделенного запятыми, возвращает логическое значение для , в то время как цикл ?

Ответы [ 5 ]

17 голосов
/ 16 июля 2009

Оператор запятой является двоичным оператором, который оценивает свой первый операнд и отбрасывает результат, затем он оценивает второй операнд и возвращает это значение.

Это также «точка последовательности» , что означает, что все побочные эффекты будут рассчитаны до выполнения следующей части кода.

10 голосов
/ 16 июля 2009

Оператор запятой - странное чудовище, пока вы не поймете его, и оно не относится только к 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 использует это возвращаемое значение для управления циклом.
4 голосов
/ 16 июля 2009

Во многих языках запятая является оператором, который всегда приводит к значению второго операнда. Операнды оцениваются последовательно слева направо.

Псевдо-код:

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)
2 голосов
/ 16 июля 2009

Чтобы немного расширить другие ответы, в этом коде:

EXPRESSION_1 , EXPRESSION_2

EXPRESSION_1 сначала оценивается, затем идет точка последовательности, затем EXPRESSION_2 оценивается, и значением всего этого является значение EXPRESSION_2.

Порядок гарантии и точка последовательности важны для указанного вами кода. Вместе они означают, что мы можем быть уверены, что функция getchar () будет вызвана и значение переменной c будет полностью обновлено до того, как будет проверено значение c.

1 голос
/ 16 июля 2009

запятая является оператором. Возвращает значение правого выражения по умолчанию . Порядок оценки гарантированно будет слева, а затем справа.

ОБНОВЛЕНИЕ (ответ на комментарий Пакса):

Как и большинство операторов, он может быть перегружен для пользовательских типов:

#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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...