;
分隔的一系列语句。当您想要进行某些分支时,您可以使用if
语句。您还可以使用循环和其他类型的控制传输语句。,
运算符发挥作用的地方。运算符,
在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语言的逗号并不是一个好的编码风格,因为它太容易被忽略了 - 无论是别人试图阅读/理解/修复你的代码,还是你自己一个月后再来看。当然,在变量声明和for循环之外,它是惯用的。
你可以使用它,例如将多个语句打包到三元运算符(?:)中,如:
int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;
但是我的天啊,为什么?!? (我在真实的代码中看到它被用于这种方式,但不幸的是没有访问权限来展示)
op?:
的第三个操作数上加括号,否则绑定将是:int x = (cond ? A : B), 117;
xD - Johannes Schaub - litb,
运算符连接成较大表达式的子表达式。在C语言中,“语句”是一个重要的概念,误用该术语只会导致不必要的混淆。 - AnT stands with Russiaconst int x; if(some_bool) { printf("WTF"); x=5; } else { fprintf(stderr, "No, really, WTF"); x=117; }
。尽管如此,我同意你的例子不太美观或易读。 - AdisakC++中的两个很厉害的逗号运算符特性:
a) 读取流直到遇到特定字符串(有助于保持代码DRY):
...
while (cin >> str, str != "STOP") {
//process str
}
b) 在构造函数初始化器中编写复杂的代码:
class X : public A {
X() : A( (global_function(), global_result) ) {};
};
while (cin >> str && str != "")
,虽然可能还有其他类似的用法。 - UncleBenscin >> str
返回的是iostream
,当到达文件结尾时会转换为false
布尔值,而不是遇到空字符串时! - P Shved我曾经在宏中看到过这个用法,其中宏假装成函数并希望返回一个值,但需要先进行一些其他的操作。虽然这种做法总是很丑陋,而且通常看起来像是一种危险的黑科技。
以下是一个简化的示例:
#define SomeMacro(A) ( DoWork(A), Permute(A) )
在这里,B=SomeMacro(A)
将Permute(A)的结果作为"返回值"赋值给"B"。
Boost Assignment库是一个很好的例子,展示了如何以有用、易读的方式对逗号运算符进行重载。例如:
using namespace boost::assign;
vector<int> v;
v += 1,2,3,4,5,6,7,8,9;
我必须使用逗号调试互斥锁,在锁开始等待之前放置一条消息。
我不能将日志消息放在派生锁构造函数的主体中,因此我必须将其放在基类构造函数的参数中,使用初始化列表中的 : 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__ )
{
}
....
};
根据C标准:
逗号运算符的左操作数作为void表达式进行求值;在它的求值之后有一个序列点。然后对右操作数进行求值; 结果具有其类型和值。(逗号运算符不会产生lvalue。)如果试图修改逗号运算符的结果或在下一个序列点之后访问它,则行为是未定义的。
简而言之,它允许您在C语言只期望一个表达式的位置指定多个表达式。但实际上它主要用于for循环。
请注意:
int a, b, c;
int a, b, c;
中没有“初始化器”。在 int a, b, c;
中,逗号分隔声明符。 - AnT stands with Russiaint a[] = {1, 2, 3}
。 - Nicolas Goy有时它被用于宏中,例如像这样的调试宏:
#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))
(但是看看这个可怕的失败案例,由我亲手制造,它展示了过度使用会带来什么后果。)
但是,除非你真正需要它,或者你确信它可以使代码更易读和易于维护,否则我建议不要使用逗号运算符。
除了for循环之外,在其他地方使用逗号操作符可能会有代码异味,我唯一认为逗号操作符可以很好地被使用的地方是作为delete的一部分:
delete p, p = 0;
与其他选项相比,唯一的优势是如果这个操作分成两行,你可能会不小心只复制/粘贴其中一半。
我也喜欢它,因为如果你养成了习惯,你就永远不会忘记零赋值。(当然,为什么 p 没有被包装在 auto_ptr、smart_ptr、shared_ptr 等类型的指针中是一个不同的问题。)