在 C 和 C + + 中用逗号代替分号的效果

我已经注意到,在重构 C 和 C + + 代码的各个部分时,使用逗号而不是分号来分隔语句。就像这样

int a = 0, b = 0;
a = 5, b = 5;

在我期待的地方

int a = 0, b = 0;
a = 5; b = 5;

我知道 C 和 C + + 允许使用逗号来分隔语句(特别是循环头) ,但是这两段代码之间有什么区别呢?我猜测逗号是剪切和粘贴的结果,但它是一个 bug 吗? 它会影响执行吗?

24514 次浏览

逗号是一个运算符,它返回的值总是第二个(右)参数,而分号只是结束语句。这允许在其他语句中使用逗号运算符,或者将多个语句连接为一个语句。

这里调用函数 f (x) ,然后为 if 语句计算 x > y

if( y = f(x), x > y )

一个例子,当它只是用来避免一个块的需要

if( ... )
x = 2, y = 3;


if( ... ) {
x = 2;
y = 3;
}

这对你发布的代码没有影响。一般来说,逗号分隔表达式就像分号一样,但是,如果将整体作为表达式,那么逗号运算符意味着表达式的计算结果为最后一个参数。

这里有一个例子:

b = (3, 5);

将计算3,然后5,并将后者赋值给 b。因此 b = 5。注意括号在这里很重要:

b = 3, 5;

将计算 b = 3,然后是5,整个表达式的结果是5,但是 b == 3

当迭代器代码不是简单的 i++,但需要执行多个命令时,逗号操作符在 for-loop 中特别有用。在这种情况下,分号不能很好地与 for 循环语法一起工作。

一种用法是代码高尔夫:

if (x == 1) y = 2, z = 3;
if (x == 1) { y = 2; z = 3; }

第一行比较短,但是在常规开发中看起来太混乱了。

逗号运算符从左到右计算所有操作数,结果是最后一个操作数的值。

如果您希望在“增量”部分执行多个操作,例如(逆转字符串) ,那么在 for 循环中它最为有用

for (int lower = 0, upper = s.size() - 1; lower < upper; ++lower, --upper)
std::swap(s[lower], s[upper]);

另一个例子,它可能是一个选项(查找字符串中的所有匹配项) :

#include <string>
#include <iostream>


int main()
{
std::string s("abracadabra");
size_t search_position = 0;
size_t position = 0;


while (position = s.find('a', search_position), position != std::string::npos) {
std::cout << position << '\n';
search_position = position + 1;
}
}

特别是,逻辑 还有不能用于此条件,因为零和非零都可能意味着字符在字符串中找到。另一方面,使用逗号时,每次计算条件时都会调用 position = s.find(),但是这部分条件的结果被忽略。

当然,还有其他写循环的方法:

while ((position = s.find('a', search_position)) != std::string::npos)

或者只是

while (true) {
position = s.find('a', search_position);
if (position == std::string::npos)
break;
...
}

作为 弗兰克提到过,在示例中如何使用逗号运算符并不会导致 bug。逗号运算符可能会混淆,原因有以下几点:

  • 这种情况不常见,因为只有在特殊情况下才有必要
  • 逗号还有其他一些看起来像逗号运算符的语法用法——但它们不是(用于分隔函数参数/参数的逗号,用于分隔变量声明或初始化器的逗号)

由于逗号操作符容易混淆,而且常常是不必要的,因此除了一些非常特殊的情况外,应该避免使用逗号操作符:

  • 在一个或多个 for语句的控制表达式中执行多个操作可能很有用
  • 它可以在预处理器宏中用于计算单个语句中的多个表达式。这样做通常是为了允许宏执行多个操作,同时仍然是单个表达式,这样宏将“适合”只允许表达式的位置。

根据定义,逗号操作符是一个 hackish 操作符——它是指在只允许一个操作符的情况下对两个事物进行黑客操作。几乎总是丑陋的,但有时这就是你的全部。这是你唯一应该使用它的时候-如果你有其他选择,不要使用逗号运算符。

我想不出还有什么其他理由使用这个操作符,因为在大多数其他情况下,你可以通过在单独的语句中计算表达式来获得类似的效果(尽管我确信有人会对我忽略的另一个用法进行评论)。