在while循环条件下分配值

我在维基百科上找到了这段代码。

#include  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循环返回布尔值?

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

它也是一个“序列点” ,这意味着所有副作用将在下一部分代码执行之前计算出来。

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

表达方式:

 exp1, exp2 

计算exp1然后计算exp2并返回exp2

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

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

你实际上没有使用 "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使用该返回值来控制循环。

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

伪代码:

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

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

执行方式如下:

  • 第一行
    • 10放进去
  • 第二行
    • 评价7 + 815
    • 将评估值( 15 )放入a
    • 评估a * 230
    • 使用操作数1530评估操作符:
      • 始终是第二个操作数的值( 30
    • 打印评估值( 30

要在其他答案上进一步扩展,请在此代码中:

 EXPRESSION_1 , EXPRESSION_2 

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

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

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

更新(回复Pax的评论):

就像大多数操作符一样,它可以为用户定义的类型重载:

 #include  #include  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; }