在while循环条件中分配值

时间:2009-07-16 08:09:37

标签: c initialization while-loop

found维基百科上的这段代码。

#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 个答案:

答案 0 :(得分:17)

comma operator是一个二元运算符,它计算第一个操作数并丢弃结果,然后计算第二个操作数并返回该值。

它也是"sequence point",这意味着所有副作用都将在下一部分代码执行之前计算出来。

答案 1 :(得分:10)

逗号运算符是一个奇怪的野兽,直到你理解它,并不是while特有的。

表达式:

exp1, exp2

评估exp1,然后评估exp2并返回exp2

你经常看到它,虽然你可能没有意识到:

for (i = j = 0; i < 100; i++, j += 2)

你实际上并没有使用来自"i++, j += 2"的返回值,但它仍然存在。逗号运算符计算两个位以修改ij

你可以在任何可以使用普通表达式的地方使用它(例如你的函数调用中的逗号是而不是一个逗号运算符),它在编写压缩源代码时非常有用,如果这就是你喜欢的。通过这种方式,它是家庭的一部分,允许这样的事情:

while ((c= getchar()) != EOF) {...}
i = j = k = 0;

等等。

对于您的具体示例:

while (c = getchar(), c != EOF && c != 'x')

发生以下情况:

    完全执行
  • c = getchar()(逗号运算符是序列点)。
  • c != EOF && c != 'x'已执行。
  • 逗号运算符抛弃第一个值(c)并“返回”第二个值。
  • while使用该返回值来控制循环。

答案 2 :(得分:4)

在许多语言中,逗号是一个运算符,它总是产生第二个操作数的值。操作数从左到右依次进行评估。

的伪代码:

a = 10
print a = 7 + 8, a * 2

注意:print被认为是不带参数的语句,因此后面的内容被认为是单个表达式a = 7 + 8, a * 2

执行如下:

  • 第一行
    • 10放入a
  • 第二行
    • 评估7 + 815
    • 将评估值(15)放入a
    • 评估a * 230
    • 使用操作数,15评估30运算符:
      • 始终为第二个操作数(30
      • 的值
    • 打印评估值(30

答案 3 :(得分:2)

要在其他答案上进一步扩展,请使用以下代码:

EXPRESSION_1 , EXPRESSION_2

首先评估EXPRESSION_1,然后有一个序列点,然后评估EXPRESSION_2,整个事物的值是EXPRESSION_2的值。

操作保证顺序和序列点对您引用的代码都很重要。它们一起意味着我们可以确定在测试c的值之前调用getchar()函数并且完全更新变量c的值。

答案 4 :(得分:1)

逗号是一名运营商。它默认返回右手表达式的值。保证评估顺序先行,然后保持正确。

更新(回复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;
}