16

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

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

4

5 回答 5

21

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

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

于 2009-07-16T08:12:07.067 回答
12

在您理解之前,逗号运算符是一个奇怪的怪物,而且它并不特定于while.

表达方式:

exp1, exp2

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

您经常看到它,尽管您可能没有意识到:

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

您实际上并没有使用from 的返回值,"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返回值来控制循环。
于 2009-07-16T08:14:47.950 回答
4

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

伪代码:

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

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

像这样执行:

  • 第一行
    • 放入10_a
  • 第二行
    • 评估7 + 8( 15)
    • 将评估值 ( 15) 放入a
    • 评估a * 2( 30)
    • 使用操作数和评估,运算符: 1530
      • 总是第二个操作数的值 ( 30)
    • 打印评估值 ( 30)
于 2009-07-16T08:12:29.280 回答
2

要扩展其他答案,请在此代码中:

EXPRESSION_1 , EXPRESSION_2

先求EXPRESSION_1,然后有一个序列点,再求EXPRESSION_2,整个东西的值就是EXPRESSION_2的值。

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

于 2009-07-16T08:17:54.277 回答
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;
}
于 2009-07-16T08:12:18.937 回答