【问题标题】:Assigning value in while loop condition在while循环条件中赋值
【发布时间】:2010-11-11 06:55:59
【问题描述】:

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

它的作用很明显,但我以前从未见过这种结构。这是 while 循环特有的吗?如果不是,解析器/编译器如何确定逗号分隔表达式的哪一侧为 while 循环返回布尔值?

【问题讨论】:

  • 不影响你的问题,只是标题,但是:c是在while循环中分配的,没有初始化。
  • 那里 - 我已经改变了。谢谢。

标签: c initialization while-loop


【解决方案1】:

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

伪代码:

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)

【讨论】:

    【解决方案2】:

    逗号是一个运算符。 默认情况下返回右手表达式的值。求值顺序保证先左后右。

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

    【讨论】:

    • 我很好奇“默认情况下”这个短语 - 你不是建议可以配置这种行为,是吗?我认为标准很清楚这就是它的方式,不允许有任何偏差。
    • 我最初的问题是关于 C 语言的——你已经深入 C++ 水域。无论如何,重载的使用非常有趣。
    • Josip:哦,我没注意到 C 标签。我的原始答案适用于两者,但我对 Pax 评论的回复绝对是 C++。
    • 抱歉,@Mehrdad,我没有想到 C++ 的角度。
    【解决方案3】:

    在您理解之前,逗号运算符是一个奇怪的怪物,而且它并不特定于 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 &amp;&amp; c != 'x' 被执行。
    • 逗号运算符丢弃第一个值 (c) 并“返回”第二个值。
    • while 使用该返回值来控制循环。

    【讨论】:

    • 如果我想在一个表达式中链接多个逗号运算符,不带括号这样做是否安全?
    • 是的,逗号运算符的优先级最低。
    • @Josip,我会小心的,逗号运算符的优先级很低,应该可以,但你可能会发现自己有意想不到的结果。虽然我认为你可以在任何允许表达的地方使用它,但这并不一定意味着你应该
    【解决方案4】:

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

    EXPRESSION_1 , EXPRESSION_2
    

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

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

    【讨论】:

      【解决方案5】:

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

      也是"sequence point",表示在执行下一部分代码之前会计算所有副作用。

      【讨论】:

      • 只是补充一下,因为它可能并不明显——“=”运算符更新变量 c 的值的效果在这种情况下被认为是“副作用”。
      • 正确 - 因为逗号运算符的优先级最低
      猜你喜欢
      • 2011-12-08
      • 2013-09-25
      • 2016-01-13
      • 2013-11-15
      • 2013-02-17
      • 1970-01-01
      • 2015-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多