C++ 变量赋值在";如果;条件

C++ 变量赋值在";如果;条件,c++,if-statement,C++,If Statement,我最近花了一些时间来找出我的代码中的一个错误,它是由一个打字错误引起的: if (a=b) 而不是: if (a==b) 我想知道在if语句中是否有任何特殊情况需要为变量赋值,或者如果没有,为什么编译器不抛出警告或错误?赋值运算符返回赋值的值。所以,我可能会在这样的情况下使用它: if (x = getMyNumber()) 我将x指定为getMyNumber返回的值,并检查它是否为零 避免那样做,我给你举个例子只是为了帮助你理解这一点 编辑:仅添加建议 为了避免在某些扩展中出现此类错误,

我最近花了一些时间来找出我的代码中的一个错误,它是由一个打字错误引起的:

if (a=b)
而不是:

if (a==b)

我想知道在
if
语句中是否有任何特殊情况需要为变量赋值,或者如果没有,为什么编译器不抛出警告或错误?

赋值运算符返回赋值的值。所以,我可能会在这样的情况下使用它:

if (x = getMyNumber())
我将
x
指定为
getMyNumber
返回的值,并检查它是否为零

避免那样做,我给你举个例子只是为了帮助你理解这一点

编辑:仅添加建议

为了避免在某些扩展中出现此类错误,应该将if条件写为
if(NULL==ptr)
而不是
if(ptr==NULL)
,因为当您将等式检查操作符
==
拼写错误为操作符
=
时,编译将抛出一个左值错误
if(NULL=ptr)
,但是
if(res=NULL)
由编译器传递(这不是您的意思),并且在运行时仍然是代码中的一个bug


人们也应该阅读关于这类代码的内容。

if
中做作业是一件相当常见的事情,尽管人们意外地做作业也是很常见的

通常的模式是:

if (int x = expensive_function_call())
{
  // ...do things with x
}
反模式是指您错误地分配了以下内容:

if (x = 1)
{
  // Always true
}
else
{
  // Never happens
}
通过将常量或
const
值放在第一位,可以在一定程度上避免这种情况,因此编译器将抛出错误:

if (1 = x)
{
  // Compiler error, can't assign to 1
}

=
vs.
=
是你需要培养眼睛的东西。我通常在操作符周围加上空格,以便更清楚地看到正在执行的操作,因为
longname=longername
看起来很像
longname==longername
,但是
=/code>和
=
本身就是obvi完全不同。

这取决于您是否要编写干净的代码。何时 C是最早被开发出来的,干净代码的重要性 没有得到充分的认可,而且编译器非常简单: 像这样使用嵌套赋值通常会导致更快的 今天,我想不出任何一个好的程序员 可以。它只会使代码可读性变差,更容易理解 难以维护

为什么编译器不抛出警告

有些编译器会为条件表达式中的可疑赋值生成警告,尽管通常必须显式启用警告

例如,在VisualC++中,必须启用(或一般级别4级警告)。通常我会打开尽可能多的警告,使代码更明确,以避免误报。例如,如果我真的想这样做:

if (x = Foo()) { ... }
if(Bar* myBar = getBar()) {
然后我会写为:

if ((x = Foo()) != 0) { ... }
编译器看到显式测试,并假设赋值是有意的,所以这里不会出现误报警告

这种方法的唯一缺点是,在条件中声明变量时不能使用它。也就是说,不能重写:

if (int x = Foo()) { ... }
作为

从语法上讲,这是行不通的。因此,您要么禁用警告,要么在范围
x
的严格程度上妥协

更新:C++17在if语句()的条件中添加了init语句的功能,这很好地解决了这个问题(用于某种比较,而不仅仅是
!=0

此外,如果需要,可以将
x
的范围仅限于if语句:

if (int x = Foo(); x != 0) { /* x in scope */ ... }
// x out of scope
if(派生*Derived=dynamic_cast(基本)){
//用“派生”来做事情`
}

尽管这经常被称为反模式(“使用虚拟调度!”),但有时
派生的
类型具有
所不具备的功能(因此也具有不同的功能),这是一种很好的方式来切换语义差异。

下面是有关语法的一些历史

在经典C语言中,错误处理通常是通过编写以下内容来完成的:

int error;
...
if(error = foo()) {
    printf("An error occured: %s\nBailing out.\n", strerror(error));
    abort();
}
或者,每当有可能返回空指针的函数调用时,习惯用法就反过来使用:

Bar* myBar;
... //in old C variables had to be declared at the start of the scope
if(myBar = getBar()) {
    //do something with myBar
}
但是,这种语法非常接近于

if(myValue == bar()) ...

这就是为什么许多人认为赋值在一个条件不好的情况下,编译器开始警告它(至少用<代码>墙>代码>)。一些编译器允许通过添加额外的括号来避免这个警告:

if((myBar = getBar())) {  //tells the compiler: Yes, I really want to do that assignment!
然而,这很难看,有点像黑客,所以最好避免编写这样的代码

然后C99出现了,它允许您混合定义和语句,因此许多开发人员经常编写类似于

Bar* myBar = getBar();
if(myBar) {
这确实让人感到尴尬。这就是为什么最新标准允许在条件中定义,以提供一种简洁、优雅的方式来实现这一点:

if (x = Foo()) { ... }
if(Bar* myBar = getBar()) {
此语句不再存在危险,您显式地为变量指定了一个类型,显然希望对其进行初始化。它还避免了定义变量的额外行,这很好。但最重要的是,编译器现在可以轻松捕获此类错误:

if(Bar* myBar = getBar()) {
    ...
}
foo(myBar->baz);  //compiler error
myBar->foo();     //compiler error
如果
if
语句中没有变量定义,则无法检测到这种情况


长话短说:你的问题中的语法是旧C语言简单和强大的产物,但它是邪恶的,因此编译器可以对此发出警告。因为它也是一种非常有用的表达常见问题的方式,现在有一种非常简洁、bug健壮的方式来实现同样的行为。它有很多好的、可能的用途。

我最近遇到了一个案例,这个案例很有用,所以我想我应该发布它

假设要在单个if中检查多个条件,如果其中任何一个条件为真,则要生成错误消息。如果要在错误消息中包含导致错误的特定条件,
if(Bar* myBar = getBar()) {
    ...
}
foo(myBar->baz);  //compiler error
myBar->foo();     //compiler error
std::string e;
if( myMap[e = "ab"].isNotValid() ||
    myMap[e = "cd"].isNotValid() ||
    myMap[e = "ef"].isNotValid() )
{
    // here, e has the key for which the validation failed
}
if (<initialize> ; <conditional_expression>) { <body> }
if (Employee employee = GetEmployee(); employee.salary > 100) { ... }