C语言 逗号运算符的正确用法是什么?

uplii1fm  于 2023-10-16  发布在  其他
关注(0)|答案(9)|浏览(137)

我看到了这个代码:

if (cond) {
    perror("an error occurred"), exit(1);
}

你为什么要这么做?为什么不干脆:

if (cond) {
    perror("an error occurred");
    exit(1);
}
0vvn1miw

0vvn1miw1#

在你的例子中,它根本没有理由。有时候写成

if(cond)
  perror("an error occured"), exit(1) ;

--那么你就不需要**花括号了。但这是一个邀请灾难。
逗号运算符是将两个或多个表达式放在引用只允许一个的位置。在你的情况下,没有必要使用它;在其他情况下,例如在while循环中,它可能是有用的:

while (a = b, c < d)
  ...

while循环的实际“求值”仅由最后一个表达式控制。

x6492ojm

x6492ojm2#

逗号运算符的合法情况很少,但它们确实存在。一个例子是当你想在条件求值中发生一些事情时。例如:

std::wstring example;
auto it = example.begin();
while (it = std::find(it, example.end(), L'\\'), it != example.end())
{
    // Do something to each backslash in `example`
}

它也可以用在你只能放置一个表达式,但希望发生两件事的地方。例如,下面的循环在for循环的第三个组件中递增x并递减y:

int x = 0;
int y = some_number;
for(; x < y; ++x, --y)
{
    // Do something which uses a converging x and y
}

不要去寻找它的用途,但如果它是适当的,不要害怕使用它,不要被抛出一个循环,如果你看到别人使用它。如果你有两个东西,没有理由不分开的陈述,使他们分开的陈述,而不是使用逗号运算符。

8cdiaqws

8cdiaqws3#

逗号运算符的主要用途是混淆;它允许做两件事,而读者只期望一件事。最常见的用途之一--在一种情况下增加副作用,福尔斯就属于这一类。然而,有几种情况可能被认为是有效的:
在K&R中用来呈现它的那个:在for循环中递增两个变量。在现代代码中,这可能发生在像std::transformstd::copy这样的函数中,其中输出迭代器与输入迭代器同步递增。(当然,这些函数更经常地包含一个while循环,在循环结束时,增量在单独的语句中。在这种情况下,使用逗号而不是两个语句是没有意义的。
另一种情况是初始化列表中输入参数的数据验证:

MyClass::MyClass( T const& param )
    : member( (validate( param ), param) )
{
}

(This假设validate( param )在出现错误时会抛出异常。)这种用法并不特别吸引人,尤其是当它需要额外的括号时,但替代方法并不多。
最后,我有时会看到这样的约定:

ScopedLock( myMutex ), protectedFunction();

,这就避免了为ScopedLock发明一个名字。说实话,我不喜欢它,但我看到过它的使用,并且添加额外的括号以确保ScopedLock立即被析构的替代方案也不是很漂亮。

iyzzxitl

iyzzxitl4#

举几个例子可以更好地理解这一点:

**首先:**考虑一个表达式:

x = ++j;

但是暂时,如果我们需要分配一个临时的调试值,那么我们可以这样写。

x = DEBUG_VALUE, ++j;

第二次:

逗号,运算符经常在for()-loop中使用,例如:

for(i = 0, j = 10; i < N; j--, i++) 
 //      ^                   ^     here we can't use ;

第三:

再举一个例子(实际上人们可能会觉得这样做很有趣):

if (x = 16 / 4), if remainder is zero then print  x = x - 1;  
if (x = 16 / 5), if remainder is zero then print  x = x + 1;

它也可以在一个步骤中完成;

if(x = n / d, n % d) // == x = n / d; if(n % d)
    printf("Remainder not zero, x + 1 = %d", (x + 1));
  else
    printf("Remainder is zero,  x - 1 = %d", (x - 1));

**PS:**可能也很有趣,知道有时使用,运算符是灾难性的。例如,在Strtok用法的问题中,代码不工作,由于错误,OP忘记写函数的名称,而不是写tokens = strtok(NULL, ",'");,他写了tokens = (NULL, ",'");,他没有得到编译错误-但它是一个有效的表达式,tokens = ",'";在他的程序中导致了无限循环。

2ic8powd

2ic8powd5#

逗号运算符允许在需要表达式的地方对表达式进行分组。
例如,它在某些情况下可能是有用的:

// In a loop
while ( a--, a < d ) ...

但在你的情况下,没有理由使用它。会很混乱...就这样
在你的例子中,这只是为了避免花括号:

if(cond)
    perror("an error occurred"), exit(1);

// =>
if (cond)
{
    perror("an error occurred");
    exit(1);
}

comma operator文档的链接。

mwyxok5s

mwyxok5s6#

操作符()的实际用途似乎很少。
Bjarne Stroustrup,《C++的设计与演化》
大多数逗号的常用用法可以在维基百科文章Comma_operator#Uses中找到。
在使用boost::assign时,我发现了一个有趣的用法,它明智地重载了操作符,使其表现为一个逗号分隔的值列表,可以将其推到向量对象的末尾

#include <boost/assign/std/vector.hpp> // for 'operator+=()'
using namespace std;
using namespace boost::assign; // bring 'operator+=()' into scope

{
    vector<int> values;  
    values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container
}

不幸的是,一旦编译器开始支持Uniform,上述在原型设计中流行的用法现在看起来就过时了。
所以我们又回到了
操作符()的实际用途似乎很少。
Bjarne Stroustrup,《C++的设计与演化》

c86crjj0

c86crjj07#

在你的例子中,逗号操作符是无用的,因为它可以用来避免花括号,但事实并非如此,因为作者已经把它们放在了。因此,它是无用的,可能会混淆**。

mwg9r5ms

mwg9r5ms8#

如果您希望在条件为truefalse时执行两条或多条指令,则它对行程操作符很有用。但请记住,返回值将是最右边的表达式,因为逗号运算符从左到右的求值规则(我的意思是在括号内)
例如:

a<b?(x=5,b=6,d=i):exit(1);
sqougxex

sqougxex9#

boost::assign重载了逗号运算符,以实现这种语法:

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;

相关问题