C# 컴파일러는 변수를 초기화시키지 않을까요?
아래와 같은 질문이 있습니다.
기본 질문, 변수 초기화
; https://www.sysnet.pe.kr/3/0/1234
질문을 정리해 볼까요? 다음과 같은 코드가 있는 경우,
using System;
class Program
{
static void Main(string[] args)
{
string txt;
Console.WriteLine(txt);
}
}
C# 컴파일러는 다음과 같은 오류와 함께 컴파일을 중지합니다.
error CS0165: Use of unassigned local variable 'txt'
이 오류를 없애려면 다음과 같이 명시적으로 초기화를 해야만 C# 컴파일러는 정상적으로 어셈블리를 생성합니다.
string txt = null;
Console.WriteLine(txt);
그렇다면 C# 컴파일러는 txt 변수를 null로 초기화시키는 것이 아닐까요? 현상으로만 봐서는 개발자가 명시적으로 하는 듯 합니다.
이를 정확하게 확인하려면 IL 코드 수준으로 내려가야 합니다. 위에서 컴파일된 EXE 파일을 ildasm.exe를 이용해 다음과 같이 IL 코드로 변경합니다.
c:\temp>ildasm ConsoleApplication1.exe /OUT=test.il
그럼, test.il 파일에 Main 코드를 볼 수 있는데요. 개발자가 null로 명시적인 초기화를 하는 코드가 추가된 부분이 포함되어 있습니다.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string txt)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: call void [mscorlib]System.Console::WriteLine(string)
IL_0009: nop
IL_000a: ret
} // end of method Program::Main
이걸 지우고 컴파일하면 어떻게 될까요? 만약, 닷넷 프로그램이 변수 초기화를 해주지 않는다면 WriteLine의 출력값은 C/C++처럼 쓰레기값이 나오거나 운이 나쁘다면 프로그램이 비정상종료될 것입니다.
일단 제거하고,
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string txt)
IL_0000: nop
IL_0003: ldloc.0
IL_0004: call void [mscorlib]System.Console::WriteLine(string)
IL_0009: nop
IL_000a: ret
} // end of method Program::Main
컴파일하면,
c:\temp>ilasm test.il /OUT=test.exe
Microsoft (R) .NET Framework IL Assembler. Version 4.0.30319.33440
Copyright (c) Microsoft Corporation. All rights reserved.
Assembling 'test.il' to EXE --> 'test.exe'
Source file is ANSI
Assembled method Program::Main
Assembled method Program::.ctor
Creating PE file
Emitting classes:
Class 1: Program
Emitting fields and methods:
Global
Class 1 Methods: 2;
Emitting events and properties:
Global
Class 1
Writing PE file
Operation completed successfully
아하~~~ 정상적으로 컴파일되는 군요. 게다가 실행시켜 보면 null 출력과 동일하게 화면에는 아무것도 출력되지 않습니다.
즉, C# 컴파일러는 (좀 더 정확하게 말하면 닷넷은) 기본적으로 변수에 대해 초기화되지 않은 상태에서도 0(null)로 초기화를 해둡니다. 단지, C# 컴파일러는 개발자로 하여금 명시적으로 초기화하지 않은 변수를 사용하고 있다는 것을 알리기 위해 CS0165 오류를 내는 것입니다.
이를 염두에 두고 다음의 코드를 볼까요?
using System;
class Program
{
static void Main(string[] args)
{
string txt;
if (Environment.TickCount == 0)
{
txt = Environment.TickCount.ToString();
}
Console.WriteLine(txt);
}
}
if 문 안에서 개발자가 txt 변수를 초기화했지만, C# 컴파일러는 프로그램의 실행 경로 상에 여전히 if 내부를 타지 않는 경우 txt가 초기화되지 않을 수 있으므로 이번에도 CS0165 오류를 발생시킵니다.
따라서, 이런 경우에는 다음과 같이 2가지로 고쳐주면 오류가 사라집니다.
string txt = null;
if (Environment.TickCount == 0)
{
txt = Environment.TickCount.ToString();
}
string txt;
if (Environment.TickCount == 0)
{
txt = Environment.TickCount.ToString();
}
else
{
txt = null;
}
물론, 개발자는 txt 변수가 null이라고 가정할 수 있겠지만 분명히 실수로 txt 변수가 초기화되지 않았다는 사실을 모를 수도 있습니다. C# 컴파일러가 어떻게 개발자의 마음을 읽어 그 두가지 상황을 구별할 수 있겠습니까? 대신, 그 두가지 경우에서 C# 컴파일러는 프로그램이 보다 안정적으로 돌아가는 방향으로 선택을 한 것입니다.
만약 제가 .NET 프레임워크 용 언어로 D#을 만든다면... 아마도 변수 초기화를 안했다고 CS0165 오류를 내도록 하지는 않았을 것입니다. 그런 기능까지 신경쓰기에는... ^^;
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]