Does a simple C++ application compile to the same IL as the equivalent VB or C# application in .NET? Let's see.
From Kate Gregory's Codeguru column, "Using Visual C++ .NET".
One of the really exciting things about working with .NET is the idea of a ".NET language"—a language that emits managed code and conforms to the Common Language Specification. And (at least in theory, anyway) if you write code in two or more of these languages, they will compile to the same intermediate language, or IL.
So, is C++ part of the in-crowd? Does a simple C++ application compile to the same IL as the equivalent Vb or C# application? Let's see.
Three Little Apps...
Here's a little C++ console application:
for (i=0; i<10; i++)
Here's the equivalent in C#:
static void Main(string args)
And the same thing in VB:
Dim i As System.Int32
For i = 0 To 9
It's an interesting aside to see how names are assigned. I created a blank solution and added projects to it:
- I named the C++ project SimpleC++, so I got a file called SimpleC++.cpp with a function called _tmain(). There was no namespace or class created.
- I named the C# project SimpleCSharp (file names can't have the # symbol), so I got a file called Class1.cs. It defines the namespace SimpleCSharp, containing a class called Class1 with a function called Main().
- I named the VB project SimpleVB, so I got a file called Module 1.vb. It defines the namespace SimpleVB, containing a module called Module1 with a Sub called Main().
All of these console applications do the same thing: print the numbers 0 to 9, one per line, on the console.
ILDASM is the Intermediate Language Disassembler. If you use a "Visual Studio. NET Command Prompt" (Start, Programs, Visual Studio .NET, Visual Studio .NET Tools, Visual Studio .NET Command Prompt), the environment variables will be set properly to enable you to use ILDASM and other handy utilities. From this command prompt, you can cd to the folder with an exe or dll file (a managed one, with IL in it) and take a look at the IL, like this:
The first screen shows the namespaces, classes, and modules in the assembly, along with the functions in each class or module. Here's SimpleC++ in ILDASM:
Click here for a larger image.
The small yellow S in the pink square next to main and _mainCRTStartup indicate that they are static functions. Double-clicking the main expands the IL for the function. Here it is:
.method public static int32
.CallConvCdecl) main() cil managed
.vtentry 1 : 1
.locals ( int32 i)
IL_0002: br.s IL_0008
IL_0009: ldc.i4.s 10
IL_000b: bge.s IL_0020
IL_000d: ldloca.s i
IL_000f: ldobj [mscorlib]System.Int32
IL_0014: box [mscorlib]System.Int32
IL_0019: call void [mscorlib]System.Console::
IL_001e: br.s IL_0004
IL_0021: br.s IL_0023
Now, this may look intimidating, but actually IL is human-readable. It helps if you've ever worked with assembly language, but even if you haven't, you can read this. There's a tutorial in the online help in Visual Studio that explains, for example, that ldc.i4.0 loads a four-byte zero onto the stack and stloc.0 stores the current stack value in local variable 0. It's called ILDASM Tutorial and should be easily reachable from the help index.
Here's the IL generated from the C#:
.method private hidebysig static void Main(string args)
.custom instance void [mscorlib]System.
STAThreadAttribute::.ctor() = ( 01 00 00 00 )
.locals init ( int32 i)
IL_0002: br.s IL_000e
IL_0005: call void [mscorlib]System.Console::
IL_000f: ldc.i4.s 10
IL_0011: blt.s IL_0004
This is shorter, for one thing, and it doesn't have the boxing. The for loop works differently too: The C++ checks whether i is greater than or equal to 10 and, if so, jumps past the whole loop using bge. The C# initializes i to zero, then jumps to the bottom of the loop, which checks to see whether i is less than 10 and, if so, jumps back to the top of the loop using blt. Neither of these approaches is necessarily better, but it's interesting that two superficially similar languages produce such different IL for such a simple example.
What about VB? Well, here's the IL generated from the VB code:
.method public static void Main() cil managed
.custom instance void [mscorlib]System.STAThreadAttribute:: _
.ctor() = ( 01 00 00 00 )
.locals init ( int32 i)
IL_0004: call void [mscorlib]System.Console:: _
IL_0010: ldc.i4.s 9
IL_0012: ble.s IL_0003
This code is strangely full of nop—no operation—statements, but you can see that it is very similar to the C# code. It uses ble instead of blt because VB loops continue when the index is equal to the limit—less than or equal to—but the C# code used a less-than in the limit test. (If you mess around with the loop limits, going from 10 to 9 or from 3 to 9, for example, you'll see code that looks even more like the C# code.)
The Bottom Line
What's the difference between C# and C++? They both have semicolons and brace brackets, after all. Well, that may be so, but they're very different languages. C# is far more like VB than it is like C++. And, taking a look under the hood at the IL you get from simple little applications is a good way to reveal that. What's more, because it's the IL that actually executes, any talk of faster or slower execution times needs to be underpinned with an understanding of the IL that is generated. ILDASM is a good way for you to understand what your code actually produces. Play a little!
About the Author
Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.
# # #