I'm having a hard time finding information about javac's code elimination capabilities:
I read that if you have something like the following, the if-statement will be eliminated:
static final boolean DEBUG = false;
if (DEBUG) System.out.println("Hello World!"); // will be removed
But how about this, for example:
static final int VALUE = 3;
if (VALUE > 9) System.out.println("VALUE > 9 ???"); // will this be removed?
Or this:
static final SomeEnum VALUE = SomeEnum.FOO;
if (VALUE==SomeEnum.BAR) System.out.println("Bar???"); // will this be removed?
Since it's very difficult/impossible to analyze a program to find all dead code (probably similar to the halting problem), I would imagine that there are only a few well-defined constructs (like the first example above), which javac will recognize and remove reliably. Is there a comprehensive list of these constructs?
assylias seems to have found the answer (let me just put it all together):
Chapter "14.21. Unreachable Statements" of the JLS specifies, that, in general, any unreachable statement in the code is considered a compile-time-error, with the only exception being a special treatment of if-statements to specifically allow for conditional compiles.
Therefore, the only construct that may result in code elimination (if the compiler chooses to do so!) is:
if (compileTimeConstantExpression) {
doThis(); // may be removed if compileTimeConstantExpression == false;
} else {
doThat(); // may be removed if compileTimeConstantExpression == true;
}
(the else-part is optional, of course)
All other constructs that would allow for code elimination, like for example while (false) ..., are disallowed and cause a compile-time-error instead rather than resulting in conditional compilation.
The definition for what constitutes an acceptable compileTimeConstantExpression can be found in chapter "15.28. Constant Expressions" of the JLS. Another great page with further examples can be found here: Compile Time Constants in Java
Note: There is no requirement for a compiler to remove the "unreachable" sections of an if-stament. javac seems to do this reliably, but other compilers may not. The only way to know for sure is to check the output via decompilation, for example using javap -c as suggested by Jon Skeet.
I have run a few tests and it seems (logically) that javac removes the code iif the condition is a constant expression that evaluates to false.
In summary, constant expressions are expressions that only use constants as operands, i.e. primitives, string literals and final primitives or Strings variables that have been initialised with a constant value.
Note that this is compiler dependent as the JLS does not force the compiler to be that smart as explained at the very bottom of 14.21:
An optimizing compiler may realize that the statement x=3; will never be executed and may choose to omit the code for that statement from the generated class file.
I see that for 2nd example it gets removed too
this was my class
public class Test {
public static void main(String[] args) throws Exception{
final int VALUE = 3;
if (VALUE > 9) System.out.println("VALUE > 9 ???");
}
}
and this is decompiled version
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: return
}
My guess would be that this is implementation specific (Oracle, IBM,...).
If you're interested in Oracle's version, a good place to start looking for resources would be the OpenJDK project:
http://openjdk.java.net/groups/compiler/
Related
Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();
I was doing some small programs in java. I know that if I write while(true); the program will freeze in this loop. If the code is like that:
Test 1:
public class While {
public static void main(String[] args) {
System.out.println("start");
while (true);
System.out.println("end");
}
}
The compiler throws me the error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unreachable code
at While.main(While.java:6)
I didn't know that this error exists. But I got why it is thrown. Of course, line 6 was unreachable, causing a compilation problem. Then I tested this:
Test 2:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
b();
}
static void a() {
while(true);
}
static void b() {
System.out.println("end");
}
}
For some reason the program ran normally (The console printed "start" and then froze). The compiler couldn't check inside of void a() and see that it isn't reachable. To be sure I tried:
Test 3:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
System.out.println("end");
}
static void a() {
while(true);
}
}
Same result as Test 2.
After some research I found this question. So, if the code inside the parentheses is a variable the compiler wouldn't throw the exception. That makes sense, but I don't think that the same applies to voids.
Q: So, why does the compiler just throw me the error at Test 1, if void b() (Test 2) and System.out.println("end"); (Test 3) isn't reachable?
EDIT: I tried Test 1 in C++:
#include <iostream>
using namespace std;
int main()
{
cout << "start" << endl;
while(true);
cout << "end" << endl;
return 0;
}
The compiler didn't throw any errors, then I got the same result as Test 2 and as Test 3. So I suppose this is a java thing?
The language spec has an exact definition what the compiler should treat as unreachable code, see also https://stackoverflow.com/a/20922409/14955.
In particular, it does not care about if a method completes, and it does not look inside other methods.
It won't do more than that.
However, you could get static code analysis tools like FindBugs to get a "deeper" analysis (not sure if they detect the pattern you described, though, either, and, as has been pointed out by others, the halting problem in all generality cannot be algorithmically solved anyway, so one has to draw the line at some reasonably definition of "best effort").
In general, it is not possible to determine with absolute certainly whether or not something is reachable.
Why? It is the equivalent to the Halting Problem.
The halting problem asks:
Given a description of an arbitrary computer program, decide whether the program finishes running or continues to run forever.
This problem has been proven to be unsolvable.
Whether or not X piece of code is reachable is the same as saying whether the code before it will halt.
Because it is an unsolvable problem, the compiler (in Java or any other language) doesn't try very hard to solve it. If it happens to determine that it really is unreachable, then you get the warning. If not, it may or may not be reachable.
In Java, unreachable code is a compiler error. So in order to maintain compatibility, the language spec defines exactly "how hard" the compiler should try. (Which according to the other answers, is "don't go inside another function".)
In other languages (such as C++), the compiler may go further subject to optimizations. (Unreachable code may be detected after inlining the function and discovering that it never returns.)
Unreachable code is a compile time error that simply says 'the flow of this program doesn't make sense; something will never ever be reached'.
Obviously your tests perform how they do due to an endless loop, but why does the first fail with a compile time error?
A while statement can complete normally if at least one of the
following is true:
The while statement is reachable and the condition expression is not a
constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
Alright, but what about methods invocations (such as a()) - why do tests 2 and 3 successfully compile?
An expression statement can complete normally if it is reachable.
Since a method invocation is considered an expression, they will always be reachable so long as nothing before it blocks its path of logical execution.
To better illustrate some reasoning behind this compilation mechanism, let's take an if statement, for example.
if(false)
System.out.println("Hello!"); // Never executes
The above will be correct at compile time (though many IDE's will definitely whine!).
The Java 1.7 Specification talks about this:
The rationale for this differing treatment is to allow programmers to
define "flag variables" such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible to change
the value of DEBUG from false to true or from true to false and then
compile the code correctly with no other changes to the program text.
Further, there is actually a backwards compatibility reason as well:
This ability to "conditionally compile" has a significant impact on,
and relationship to, binary compatibility (§13). If a set of classes
that use such a "flag" variable are compiled and conditional code is
omitted, it does not suffice later to distribute just a new version of
the class or interface that contains the definition of the flag. A
change to the value of a flag is, therefore, not binary compatible
with pre-existing binaries (§13.4.9). (There are other reasons for
such incompatibility as well, such as the use of constants in case
labels in switch statements; see §13.4.9.)
Most (per the spec), if not all, implementations of the Java compiler do not traverse into methods. When parsing your Java code itself, it sees a() as just a MethodInvocationElement, meaning 'This code calls other code. I really don't care, I'm just looking at syntax.'. Syntactically, it makes sense for subsequent code to belong after a call to a().
Keep in mind performance costs. Compilation already takes a considerable amount of time. In order to keep things quick, the Java compiler doesn't actually recurse into methods; that would take ages (the compiler would have to evaluate many, many paths of code -- in theory).
To further reiterate that it's syntactically driven is to add a return; statement directly after your loop in a(). Doesn't compile, does it? Syntactically, though, it makes sense without it.
The answer lies in the rules set out for reachability by the Java Language Specification. It first states
It is a compile-time error if a statement cannot be executed because it is unreachable.
And then
A while statement can complete normally iff at least one of the
following is true:
The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
and
An expression statement can complete normally iff it is reachable.
In your first example, you have a while loop that cannot complete normally because it has a condition which is a constant expression with value true and there is no reachable break within it.
In your second and third examples, the expression statement (method invocation) is reachable and can therefore complete normally.
So I suppose this is a java thing?
The rules above are Java's rules. C++ probably has its own rules, as do other languages.
In some of our companys projects code I often read something like this:
boolean foo = Boolean.FALSE;
Besides the fact that AFAIK I only have to initialize local variables in Java at all (no random values like in Pascal) and besides the fact that especially for booleans I often WANT to have an initialization, what do I miss here? Why not:
boolean foo = false;
I don't get it. And code analyzation tools like PMD and Findbugs mark it, too. But why?
Edit:
Without really knowing much about the bytecode except that it is there I created an example class and decompiled it. The Boolean.FALSE went to:
0: getstatic #15 // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
3: invokevirtual #21 // Method java/lang/Boolean.booleanValue:()Z
6: istore_1
The 'false' variant went to:
0: iconst_1
1: istore_1
So without knowing too much about this, I'd guess that more statements means more time to execute so it's not only wrong but also slower in the long run.
boolean foo = Boolean.FALSE;
This is strange and unnecessarily complicated code, written by someone who likely didn't know Java very well. You shouldn't write code like this, and PMD and FindBugs are right to mark this.
Boolean.FALSE is a java.lang.Boolean object that gets auto-unboxed; the compiler essentially translates this to:
boolean foo = Boolean.FALSE.booleanValue();
I don't have to initialize variables in Java at all...
Member variables do not need to be initialized explicitly; if you don't, they'll be initialized with a default value (which is false in the case of boolean). Local variables do need to be explicitly initialized; if you try to use a local variable without initializing it, the compiler will give you an error.
Both are same. but boolean foo = false; is enough.
There is no good reason to do this, it was probably just a novice Java programmer. I wouldn't worry too much, just replace it with false.
At the same time, you can usually if not always arrange your code such that you never declare a variable you don't have the final value for, i.e., making your objects immutable, which makes them easier to think about. What's the value of x? compared to What's the value of x between the calls to foo() and bar()? The first is generally easier to answer. This requires you to split up your classes along lines you might not be used to doing but I recommend at least trying it out.
The style to use an auto-unboxed Boolean constant in fact meshes well with the overall oververbosity endemic to many Java projects. For example:
public boolean isItOrIsItNotTheValueWeExpect(String aStringParameterThatCouldBeNull) {
boolean booleanReturnValue = Boolean.FALSE;
if (aStringParameterThatCouldBeNull != null) {
if (aStringParameterThatCouldBeNull.length() > 3) {
booleanReturnValue = Boolean.TRUE;
}
else {
booleanReturnValue = Boolean.FALSE;
}
}
else if (aStringParameterThatCouldBeNull == null) {
booleanReturnValue = Boolean.TRUE.booleanValue();
}
return booleanReturnValue;
}
Obviously, the code above would be much preferred to this unreadable mess:
public boolean validate(String s) {
return s == null? true : s.length() > 3;
}
The very occurrence of a ternary operator is considered a transgression and some projects even have it flagged by CheckStyle.
If your projects conform to such stylistic guidelines as these, that could justify your suspicious line of code.
There is no difference really although the 1st method won't work on a 1.4 or earlier JVM. The first is more convoluted since it is fetching the static value from the Boolean object and then relying on autoboxing (introduced in 1.5) to change it from a Boolean object to a boolean primitive) although I can't imagine it would ever make any speed difference.
Generally though if you are assuming a particular initial value for a variable then I would recommend initialising it rather than just declaring it as it makes the code more readable.
Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();
Logically, if(!foo) and if(foo == false) are equivalent. How are they represented in Java? Is there any difference between the two after compilation, either in the bytecode or in performance? I was unable to find an answer in the JLS, and searching brought up a lot of results about = vs. == typos and ==/equals() behavior. (In this case, the symbols hampered my searching; for future searchers, negation operator, equals false, equal to false, not condition).
To head off the CW debate: this question is NOT asking which variant people prefer or which is considered better style. I am interested in the differences in the implementation of the language, so there is a correct answer. Related-but-not-quite-a-dupe: Difference between while (x = false) and while (!x) in Java?
EDIT:
The general consensus seems to be that a good compiler should optimize these to the same thing. That makes sense and is what I suspected, but -- to ask an even MORE academic question -- is that behavior actually mandated anywhere, or is it "merely" the reasonable thing to do?
The JLS would specify the required behavior of the statements. However, how they are implemented is an implementation detail of the compiler and the JVM.
In practice, any compiler worth its salt should emit the same bytecode for those statements. And even if not, the JVM would optimize them properly.
Also, a better way to answer this, is to check for yourself, using javap:
Compile a Test.java with the following content:
class Test {
void equals(boolean f) {
if (f == false) {}
}
void not(boolean f) {
if (!f) {}
}
}
$ javac Test.java
De-assemble it:
$ javap -c Test
Compiled from "Test.java"
class Test extends java.lang.Object{
Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
void equals(boolean);
Code:
0: iload_1
1: ifne 4
4: return
void not(boolean);
Code:
0: iload_1
1: ifne 4
4: return
}
UPDATE:
Responding your question about the "academic" question. As mentioned above, the JLS is only concerned with the behavior. There is nothing in the standard that actually specifies how it should be implemented (well, JVMS provides a lot of guidance).
As long as the compiler preserves the same identical behavior, the compiler is free to implement it different, with possibility different runtime performance.
The compiler should resolve to the same code internally, so there is no difference.
try decompiling the byte code and see how different the code looks. I would guess the compile would resolve them almost identically and any slight difference would result in negligible performance difference.
OK, a more complete answer:
I would be very surprised if any compiler generated different bytecode for these variations. For anyone who's interested, it should be easy enough to check using a disassembler.
Given that both expressions (most probably) compile to the same bytecode, I expect no difference in size or performance.
The JLS says that
the expression in
an if block is evaluated, then the
result of that expression is compared to
true
In both the not case and the compare object cases, an expresion needs to be evaluated and then compared to true. If you were checking a value rather than performaing an operator on it, there may be a theoretical performance benefit as the expression evaluation becomes a no op.
But in this case, I'd expect the JIT to produce the same bytecode for both expressions.
There should be no difference in the bytecode generated for those two results and if it did unless you are creating code for a device with very limited resources (in which case you should not be writing in Java) than the difference would be negligible and you should decide which of the two ways of writing this code is a more obvious solution.
Thinking about micro-optimizations is in almost any cases a waste of time and leads to thinking in wrong ways...
I asked a similar question regarding C++ / VS2008.
Would VS2008 c++ compiler optimize the following if statement?
To avoid = vs == typos in C++, you would tend to write
if (NULL == ptr) { ... }
if (false == boo) { ... }
if (20 == num) { ... }
etc.
This is slightly less readable until you get used to it.