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


70

在重构C和C ++的各种代码时,我注意到很多时候使用逗号而不是分号来分隔语句。像这样的东西;

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

我本来期望的

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

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


12
值得一提的是,第二行中的“逗号运算符”在语义上与中界定分隔符的逗号不同int a = 0, b = 0;
mmx 2010年

2
逗号运算符还存在一个问题,它int* a, b;int* a; int* b;
JPvdMerwe 2010年

3
这就是为什么我总是将指针/引用修饰符放在变量而不是类型的旁边。int *a, b;
mskfisher 2010年

ITs只是个坏人。不要使用它。它导致难以阅读的代码。
马丁·约克2010年

1
@mskfisher:这就是为什么我从不在一个语句中声明多个变量的原因。然后,我可以将修饰符与元素类型一起放入,这与最新的强类型语言中的概念更加一致。
山姆·哈威尔

Answers:


100

它与您发布的代码没有任何区别。通常,逗号像分号一样分隔表达式,但是,如果将整体作为表达式,则逗号运算符意味着表达式的计算结果为最后一个参数。

这是一个例子:

b = (3, 5);

将求值3,然后是5,并将后者分配给b。这样b = 5。请注意,括号在这里很重要:

b = 3, 5;

b = 3尽管如此,将求值,然后5,而整个表达式的结果是5 b == 3

当您的迭代器代码不是简单的i++,但是您需要执行多个命令时,逗号运算符在for循环中特别有用。在这种情况下,分号不能与for循环语法配合使用。


1
+1,很好的解释。只想添加一个常见的陷阱,如果您要处理很多这样的陷阱,它可能会很有用:键入a[j, k]而不是a[j][k]通常会导致意外的结果。
劳拉2010年

嗯,从技术上讲,逗号是二进制运算符,因此它只有左右表达式。但是,当在同一级别上使用多个逗号时,递归结果实际上是它的计算结果为最后一个表达式。
x4u 2010年

1
这对于有时通过分配bool来运行void-init函数(在main之前)很有用:bool s_dummy =(init_my_stuff(),true);
Macke 2010年

@Mike Seymour很高兴知道-我在大学时(7年前)在我从pascal切换到C时见过-调试起来非常恐怖。
劳拉

@Mike只是要补充-a [j,k]在Visual Studio 2008编译器中编译良好,并且我希望逗号运算符可以评估直到最后一个参数,我才等于a [k]。但是在gcc中,我遇到了运行时错误!- ideone.com/vDdl5
ZoomIn

26

逗号是一个运算符,它返回的值始终是第二个(右)参数,而分号只是结束语句。这样就可以在其他语句中使用逗号运算符,或将多个语句连接起来以显示为一个。

在这里调用函数f(x),然后x > y为if语句求值。

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

仅用于避免需要块的示例

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

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

9

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

如果要在“增量”部分中执行多个操作,例如,(反转字符串),它在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;
    ...
}

5

正如Frank所提到的,在示例中如何使用逗号运算符不会引起错误。逗号运算符可能由于以下几个原因而令人困惑:

  • 它不经常出现,因为仅在某些特殊情况下才需要
  • 逗号还有其他几种句法用法,看起来像逗号运算符-但不是(逗号分隔函数参数/参数,逗号分隔变量声明或初始化程序)

由于它令人困惑且通常是不必要的,因此应避免使用逗号运算符,除非有非常特殊的情况:

  • 在一个或多个for语句的控制表达式中执行多个操作可能很有用
  • 它可以在预处理器宏中使用,以在单个语句中评估多个表达式。通常这样做是为了允许宏执行多项操作,并且仍然是单个表达式,因此宏将“适合”仅允许表达式的位置。

从定义上说,逗号运算符实际上是一种hackish运算符-它只允许黑客入侵两件事。这几乎总是很难看的,但是有时候这就是你所拥有的。这是您唯一应该使用它的时间-如果您还有其他选择,请不要使用逗号运算符。

我想不起使用该运算符的其他太多原因,因为在大多数其他情况下,您可以通过在单独的语句中评估这些表达式来获得类似的效果(尽管我确信有人会对此发表评论)我忽略的另一种用途)。


4

一种用法是在代码高尔夫中使用:

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

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


4
...无论谁编写这样的代码,都将很快得到他们应得的。
SF。

为了强调逗号对分号的影响,第二行可能在y和z之间使用分号,例如{y=2;z=3;}
mskfisher 2010年

@mskfisher:谢谢您的注意,我输错了第二个示例
时装表演
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.