본문 바로가기

진리는어디에/C#

[C#] IL(Intermediate Language)

IL(Intermediate Language)

이미지 출처 : https://jbhs7014.tistory.com/74

여러분의 PC를 보면 CPU가 있을 것이고 그 위에 운영체제가 설치 되어있을 것이다.

만일 여러분이 C/C++과 같은 언어로 프로그램을 작성한 다음 컴파일 하면 실행가능한 실행 파일. 즉, CPU가 이해하는 기계어 코드로 구성된 실행 파일이 나온다. 이런 실행 파일의 특징은 특정 CPU와 OS에서만 동작하는 실행파일이다. 

C/C++로 작성된 프로그램의 문제점
특정 환경(예를 들어 인텔 CPU, Windows OS)에서만 동작한다.

다른 환경에서 사용하려면 다시 컴파일 하거나 소스 코드 자체를 다시 작성해야 한다.

C#의 경우에는 C# 컴파일러로 컴파일하고 나면 CPU와 OS에 독립적인 기계어 코드가 생성된다. 이것을 중간언어, IL(Intermedidate Language)이라고 한다. 옛날에는 MS에서 나온 IL이라고 MSIL고 많이 불렀는데 요즘은 Common Intermediate Language, CIL 이라고 한다.

이미지 출처 : https://mplnsr.wordpress.com/2011/02/21/switching-clr-versions-in-iis/

이 중간 언어는 CPU가 바로 해석 할 수 있는 것이 아니라 "가상 머신"이라는 것이 CPU가 이해 할 수 있도록 해석해준다. 이런 구조의 장점은 CPU와 OS에 독립적으로 프로그래밍 할 수 있다는 것이다. 우리가 플랫폼 독립적으로 개발한 프로그램을 각 플랫폼에 맞는 가상 머신, 예를 들어 윈도우용 가상머신, 리눅스용 가상머신을 사용하면 어디에서든지 실행이 가능하다는 것이다.

CPU와 OS에 독립적인 기계어 코드가 생성, 가상머신만 있다면 어떤 환경에서든 실행 가능

게다가 C#만 IL 코드를 만들 수 있는 것이 아니다., C++로 프로그램을 작성하더라도 일반 컴파일러가 아닌 .NET C++ 컴파일러를 이용하면 IL을 만들어 준다. 심지어 VB도 IL로 만들 수 있다.

여러분은 IL언어로 만들어진 프로그램을 만들기 위해서 굳이 C#만 사용할 필요가 없고 C++, VB등 컴파일러가 지원만 한다면 어떤 언어든 사용 할 수 있다.

이 가상머신의 이름을 "CLR(Common Language Runtime)"이라고 한다.

IL에 대한 이해

물론 C# 프로그래밍을 하는 IL까지 알아야 할 필요는 없다. 하지만 IL을 조금이라도 이해하고 있는 것과 아예 모르는 것은 C# 문법을 이해함에 있어서 큰 차이가 있다. 이번 강의에서는 간단하게나마 IL을 이해하는데 시간을 할애해 보도록 하겠다.

과연 IL을 우리가 알게 된다면 무엇이 달라질까? 아래 예제를 살펴 보자.

using System;

struct Point
{
    public int x;
    public int y;
}

class Program
{
    static void Main(string[] args)
    {
        Point pt1;
        Point pt2 = new Point();
    }
}

Point 구조체를 선언하고, Main에서 pt1과 pt2를 선언했다. pt1은 단시 선언만, pt2는 초기화 까지. 이렇게 되면 코드에 어떤 차이가 있을까? 이때 IL을 살펴 보면 두 코드의 차이점을 명확하게 알 수 있다.

※ IL 코드를 확인하기 위해서는 IL 디스어셈블러(ildasm.exe)를 사용해야 한다. IL 디스어셈블러에 대한 내용은 [여기]에 정리 되어 있다.

.method private hidebysig static void  Main(string[] args) cil managed
{
    .entrypoint
    // 코드 크기       10 (0xa)
    .maxstack  1
    .locals init (valuetype Point V_0,
             valuetype Point V_1)
    IL_0000:  nop
    IL_0001:  ldloca.s   V_1
    IL_0003:  initobj    Point
    IL_0009:  ret
} // end of method Program::Main

IL 코드를 풀어 보면 위와 같다.

먼저 Main함수 안, 6라인에서 valuetype이  Point인 V_0과 V_1 지역 변수를 만든다. pt1, pt2변수의 이름이 V_0, V_1로 변경 되긴 했는데 IL로 컴파일 되면서 변수 이름은 컴파일러가 지정한다.

10라인에서 V_1에 대해서는 initobj를 호출 한다. 이걸 호출하면 V1이 가진 모든 멤버를 0으로 초기화 한다. 그런데 V_0에 대해서는 이런 과정이 없다. 만일 여러분이 이제 Console.WriteLine($"{pt2.x}") 를 하게 되면 pt2는 이미 0로 초기화 되었으므로 아무런 문제 없이 출력 된다.

하지만 pt1을 출력하게 되면 할당 되지 않은 필드라는 오류를 발생 시킨다.

마치며

IL의 정의와 IL을 사용하는 이유, 그리고 간단히 IL 코드를 살펴 보았다. 기회가 된다면 IL 코드를 이용해 직접 프로그래밍을 하는 샘플을 만들어 보도록하고 오늘은 이만 마칠까 한다.

부록 1. 같이 읽으면 좋은 글

 

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!