Microsoft & .NET.NET.NET Under the Hood: a Little ILDASM

.NET Under the Hood: a Little ILDASM

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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:

int _tmain(void)
{
    System::Int32 i;
    for (i=0; i<10; i++)
    {
        Console::WriteLine(__box(i));
    }
    return 0;
}

Here’s the equivalent in C#:

static void Main(string[] args)
{
    System.Int32 i;
    for (i=0;i<10;i++)
    {
       Console.WriteLine(i);
    }
}

And the same thing in VB:

Sub Main()
    Dim i As System.Int32
    For i = 0 To 9
        Console.WriteLine(i)
    Next
End Sub

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

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:

ildasm simplec++.exe

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:

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
        modopt([mscorlib]System.Runtime.CompilerServices
        .CallConvCdecl) main() cil managed
{
  .vtentry 1 : 1
  // Code size       36 (0x24)
  .maxstack  2
  .locals ([0] int32 i)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_0008
  IL_0004:  ldloc.0
  IL_0005:  ldc.i4.1
  IL_0006:  add
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  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::
                       WriteLine(object)
  IL_001e:  br.s       IL_0004
  IL_0020:  ldc.i4.0
  IL_0021:  br.s       IL_0023
  IL_0023:  ret
} // end of method 'Global Functions'::main

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)
        cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.
          STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // Code size       20 (0x14)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_000e
  IL_0004:  ldloc.0
  IL_0005:  call       void [mscorlib]System.Console::
                            WriteLine(int32)
  IL_000a:  ldloc.0
  IL_000b:  ldc.i4.1
  IL_000c:  add
  IL_000d:  stloc.0
  IL_000e:  ldloc.0
  IL_000f:  ldc.i4.s   10
  IL_0011:  blt.s      IL_0004
  IL_0013:  ret
} // end of method Class1::Main

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
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute:: _
          .ctor() = ( 01 00 00 00 ) 
  // Code size       22 (0x16)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void [mscorlib]System.Console:: _
                            WriteLine(int32)
  IL_0009:  nop
  IL_000a:  nop
  IL_000b:  ldloc.0
  IL_000c:  ldc.i4.1
  IL_000d:  add.ovf
  IL_000e:  stloc.0
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.s   9
  IL_0012:  ble.s      IL_0003
  IL_0014:  nop
  IL_0015:  ret
} // end of method Module1::Main

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.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories