c ++ - C逗号的使用

这个问题在这里已有答案:

  • 逗号运算符是做什么的?                                     9个答案

你看到它用于for循环语句,但它在任何地方都是合法的语法。 您在其他地方找到了什么用途,如果有的话?

pythonic metaphor asked 2019-09-10T21:50:46Z
20个解决方案
96 votes

C语言(以及C ++)历史上是两种完全不同的编程风格的混合,可以称之为“语句编程”和“表达式编程”。 如您所知,每种过程编程语言通常都支持诸如排序和分支之类的基本结构(参见结构化编程)。 这些基本结构以两种形式存在于C / C ++语言中:一种用于语句编程,另一种用于表达式编程。

例如,当您根据语句编写程序时,可以使用由,分隔的语句序列。如果要进行某些分支,可以使用,语句。 您还可以使用循环和其他类型的控制转移语句。

在表达式编程中,您也可以使用相同的构造。 这实际上是,运营商发挥作用的地方。 运算符,只是C中顺序表达式的分隔符,即表达式编程中的运算符,与语句编程中的;具有相同的作用。 表达式编程中的分支通过?:运算符完成,或者通过&&||运算符的短路评估属性完成。 (表达式编程虽然没有循环。要用递归替换它们,你必须应用语句编程。)

例如,以下代码

a = rand();
++a;
b = rand();
c = a + b / 2;
if (a < c - 5)
  d = a;
else
  d = b;

这是传统语句编程的一个例子,可以用表达式编程重写

a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? d = a : d = b;

或者作为

a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;

要么

d = (a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? a : b);

要么

a = rand(), ++a, b = rand(), c = a + b / 2, (a < c - 5 && (d = a, 1)) || (d = b);

毋庸置疑,在实践中,语句编程通常会产生更易读的C / C ++代码,因此我们通常使用非常好的测量和限制量的表达式编程。 但在许多情况下它会派上用场。 在可接受和不可接受之间的界限在很大程度上取决于个人偏好以及识别和阅读既定习语的能力。

另外需要注意的是:语言的设计显然是针对陈述而定制的。 语句可以自由地调用表达式,但表达式不能调用语句(除了调用预定义的函数)。 这种情况在GCC编译器中以一种相当有趣的方式改变,它支持所谓的“语句表达式”作为扩展(与标准C中的“表达式语句”对称)。 “语句表达式”允许用户直接将基于语句的代码插入到表达式中,就像它们可以将基于表达式的代码插入到标准C中的语句中一样。

另外还有一点需要注意:在C ++语言中,基于仿函数的编程起着重要的作用,可以看作是另一种形式的“表达式编程”。 根据C ++设计的当前趋势,在许多情况下,它可能比传统的语句编程更受欢迎。

AnT answered 2019-09-10T21:52:00Z
30 votes

我认为通常C的逗号不是一个好用的风格,因为它非常容易错过 - 要么是其他人试图阅读/理解/修复你的代码,要么是你自己一个月下来。 当然,在变量声明和for循环之外,它是惯用的。

例如,您可以使用它将多个语句打包成三元运算符(?:),ala:

int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;

但是我的天哪,为什么?!? (我已经看到它在实际代码中以这种方式使用,但不幸的是无法访问它)

Jack Lloyd answered 2019-09-10T21:52:38Z
21 votes

我已经看到它在宏中使用,宏假装是一个函数,想要返回一个值,但需要先做一些其他的工作。 它总是丑陋的,虽然看起来像是一个危险的黑客。

简化示例:

#define SomeMacro(A) ( DoWork(A), Permute(A) )

这里B=SomeMacro(A)“返回”Permute(A)的结果并将其分配给“B”。

Adisak answered 2019-09-10T21:53:16Z
20 votes

C ++中的两个杀手逗号运算符功能:

a)从流中读取直到遇到特定字符串(有助于保持代码DRY):

 while (cin >> str, str != "STOP") {
   //process str
 }

b)在构造函数初始化器中编写复杂代码:

class X : public A {
  X() : A( (global_function(), global_result) ) {};
};
P Shved answered 2019-09-10T21:53:54Z
10 votes

我必须使用逗号来调试互斥锁,以便在锁开始等待之前发送消息。

我不得不在派生锁构造函数体中的日志消息,所以我不得不在初始化列表中使用:baseclass((log(“message”),actual_arg))将它放在基类构造函数的参数中。 注意额外的括号。

这是类的摘录:

class NamedMutex : public boost::timed_mutex
{
public:
    ...

private:
    std::string name_ ;
};

void log( NamedMutex & ref__ , std::string const& name__ )
{
    LOG( name__ << " waits for " << ref__.name_ );
}

class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:

    NamedUniqueLock::NamedUniqueLock(
        NamedMutex & ref__ ,
        std::string const& name__ ,
        size_t const& nbmilliseconds )
    :
        boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
            boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
            ref_( ref__ ),
            name_( name__ )
    {
    }

  ....

};
fa. answered 2019-09-10T21:54:32Z
10 votes

Boost Assignment库是一个以有用,可读的方式重载逗号运算符的好例子。 例如:

using namespace boost::assign;

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;
Stjepan Rajko answered 2019-09-10T21:54:57Z
8 votes

从C标准:

逗号运算符的左操作数被计算为void表达式; 评估后有一个序列点。 然后评估右操作数; 结果有它的类型和价值。 (逗号运算符不会产生左值。))如果尝试修改逗号运算符的结果或在下一个序列点之后访问它,则行为未定义。

简而言之,它允许您指定多个表达式,其中C只需要一个。 但实际上它主要用于循环。

注意:

int a, b, c;

不是逗号运算符,它是一个声明符列表。

Sophie Alpert answered 2019-09-10T21:55:49Z
5 votes

它有时用在宏中,例如调试宏,如下所示:

#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))

(但是,真实地看看这个可怕的失败,因为当你过度时会发生什么。)

但除非你确实需要它,或者你确信它使代码更具可读性和可维护性,否则我建议不要使用逗号运算符。

Thomas Padron-McCarthy answered 2019-09-10T21:56:28Z
5 votes

你可以重载它(只要这个问题有一个“C ++”标签)。 我看过一些代码,其中重载的逗号用于生成矩阵。 或矢量,我不记得确切。 不是很漂亮(虽然有点令人困惑):

MyVector foo = 2,3,4,5,6;

SadSido answered 2019-09-10T21:56:59Z
5 votes

在for循环之外,甚至可以有代码气味的香气,我看到的唯一一个用于逗号运算符的地方是删除的一部分:

 delete p, p = 0;

如果您可以在两行上意外复制/粘贴此操作的一半,则唯一的值超过替代值。

我也喜欢它,因为如果你出于习惯,你永远不会忘记归零。 (当然,为什么p不在某些auto_ptr,smart_ptr,shared_ptr等包装器内部是一个不同的问题。)

jmucchiello answered 2019-09-10T21:57:39Z
4 votes

鉴于@Nicolas Goy对该标准的引用,听起来你可以为循环编写单行代码,如:

int a, b, c;
for(a = 0, b = 10; c += 2*a+b, a <= b; a++, b--);
printf("%d", c);

但是好上帝,伙计,你真的想以这种方式使你的C代码更加模糊吗?

PaulMcG answered 2019-09-10T21:58:11Z
3 votes

一般来说,我避免使用逗号运算符,因为它只会降低代码的可读性。 几乎在所有情况下,只做两个陈述会更简单,更清晰。 喜欢:

foo=bar*2, plugh=hoo+7;

没有明显的优势:

foo=bar*2;
plugh=hoo+7;

除了循环之外的一个地方,我在if / else构造中使用它,如:

if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...

您可以将函数放在IF之前,但如果函数需要很长时间才能运行,您可能希望避免在没有必要的情况下执行此操作,并且如果函数不应该执行除非!= 1,那么这不是 选项。 另一种方法是将IF嵌套为额外的层。 这实际上是我通常所做的,因为上面的代码有点神秘。 但我现在用逗号方式做到了这一点,因为嵌套也很神秘。

Jay answered 2019-09-10T21:58:56Z
3 votes

ASSERT宏中添加一些评论非常有用:

ASSERT(("This value must be true.", x));

由于大多数断言样式宏将输出其参数的整个文本,因此在断言中添加了一些额外的有用信息。

Eclipse answered 2019-09-10T21:59:29Z
1 votes

我经常使用它在一些cpp文件中运行静态初始化函数,以避免经典单例的懒惰初始化问题:

void* s_static_pointer = 0;

void init() {
    configureLib(); 
    s_static_pointer = calculateFancyStuff(x,y,z);
    regptr(s_static_pointer);
}

bool s_init = init(), true; // just run init() before anything else

Foo::Foo() {
  s_static_pointer->doStuff(); // works properly
}
Macke answered 2019-09-10T21:59:54Z
1 votes

对我来说,在C中使用逗号的一个非常有用的案例是使用它们来有条件地执行某些操作。

  if (something) dothis(), dothat(), x++;

这相当于

  if (something) { dothis(); dothat(); x++; }

这不是“少打字”,有时看起来很清楚。

循环也是这样的:

while(true) x++, y += 5;

当然,只有当循环的条件部分或可执行部分非常小,两到三个操作时,两者才有用。

exebook answered 2019-09-10T22:00:45Z
0 votes

我曾经见过,运算符在for循环之外使用的唯一一次是在三元语句中执行分配。 这是很久以前所以我不能记住确切的陈述,但它是这样的:

int ans = isRunning() ? total += 10, newAnswer(total) : 0;

显然,没有理智的人会编写这样的代码,但作者是一个邪恶的天才,根据他们生成的汇编代码构建c语句,而不是可读性。 例如,他有时使用循环而不是if语句,因为他更喜欢它生成的汇编程序。

他的代码非常快但不可维护,我很高兴我不再需要使用它了。

iain answered 2019-09-10T22:01:24Z
0 votes

我已经将它用于宏“将任何类型的值赋给char *指向的输出缓冲区,然后将指针递增所需的字节数”,如下所示:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

使用逗号运算符意味着宏可以在表达式中使用,也可以根据需要用作语句:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

它减少了一些重复的打字,但你必须要小心,它不会太难以理解。

请在此处查看我的过长版本的答案。

Paul Stephenson answered 2019-09-10T22:02:10Z
0 votes

它对于“代码高尔夫”来说非常方便:

Code Golf:玩多维数据集

,if(i>0)t=i,i=0;中保存了两个字符。

Community answered 2019-09-10T22:02:49Z
0 votes

qemu有一些代码在for循环的条件部分中使用逗号运算符(参见qemu-queue.h中的QTAILQ_FOREACH_SAFE)。 他们做了什么归结为以下几点:

#include <stdio.h>

int main( int argc, char* argv[] ){
  int x = 0, y = 0;

  for( x = 0; x < 3 && (y = x+1,1); x = y ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n\n", x, y );

  for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n", x, y );
  return 0;
}

...具有以下输出:

0, 1
1, 2
2, 3

3, 3

0, 1
1, 2
2, 3

3, 4

此循环的第一个版本具有以下效果:

  • 它避免了两次分配,因此减少了代码失去同步的可能性
  • 由于它使用&&,因此在最后一次迭代后不会评估赋值
  • 由于未对赋值进行求值,因此它不会尝试取消引用队列中的下一个元素(在qemu的代码中,而不是上面的代码)。
  • 在循环内部,您可以访问当前和下一个元素
Brian Vandenberg answered 2019-09-10T22:03:55Z
0 votes

在数组初始化中找到它:

在C中,如果我使用()来初始化双维数组而不是{},究竟会发生什么?

当我初始化一个数组a[][]时:

int a[2][5]={(8,9,7,67,11),(7,8,9,199,89)};

然后显示数组元素。

我明白了:

11 89 0 0 0 
0 0 0 0 0
S M answered 2019-09-10T22:04:43Z
translate from https://stackoverflow.com:/questions/1613230/uses-of-c-comma-operator