Microsoft MVP성태의 닷넷 이야기
.NET Framework: 427. C# 컴파일러는 변수를 초기화시키지 않을까요? [링크 복사], [링크+제목 복사],
조회: 18261
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 3개 있습니다.)

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 오류를 내도록 하지는 않았을 것입니다. 그런 기능까지 신경쓰기에는... ^^;




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12455정성태12/15/20208745오류 유형: 693. DLL 로딩 시 0x800704ec - This Program is Blocked by Group Policy
12454정성태12/15/20209347Windows: 184. Windows - AppLocker의 "DLL Rules"를 이용해 임의 경로에 설치한 DLL의 로딩을 막는 방법 [1]
12453정성태12/14/202010294.NET Framework: 984. C# - bool / BOOL / VARIANT_BOOL에 대한 Interop [1]파일 다운로드1
12452정성태12/14/202010500Windows: 183. 설정은 가능하지만 구할 수는 없는 TcpTimedWaitDelay 값
12451정성태12/14/20209676Windows: 182. WMI Namespace를 열거하고, 그 안에 정의된 클래스를 열거하는 방법 [5]
12450정성태12/13/202010397.NET Framework: 983. C# - TIME_WAIT과 ephemeral port 재사용파일 다운로드1
12449정성태12/11/202010780.NET Framework: 982. C# - HttpClient에서의 ephemeral port 재사용 [2]파일 다운로드1
12448정성태12/11/202012420.NET Framework: 981. C# - HttpWebRequest, WebClient와 ephemeral port 재사용파일 다운로드1
12447정성태12/10/202010530.NET Framework: 980. C# - CopyFileEx API 사용 예제 코드파일 다운로드1
12446정성태12/10/202011186.NET Framework: 979. C# - CoCreateInstanceEx 사용 예제 코드파일 다운로드1
12445정성태12/8/20208607오류 유형: 692. C# Marshal.PtrToStructure - The structure must not be a value class.파일 다운로드1
12444정성태12/8/20209394.NET Framework: 978. C# - GUID 타입 전용의 UnmanagedType.LPStruct [1]파일 다운로드1
12443정성태12/8/20209289.NET Framework: 977. C# PInvoke - C++의 매개변수에 대한 마샬링을 tlbexp.exe를 이용해 확인하는 방법
12442정성태12/4/20208140오류 유형: 691. Visual Studio - Build Events에 robocopy를 사용할때 "Invalid Parameter #1" 오류가 발행하는 경우
12441정성태12/4/20207814오류 유형: 690. robocopy - ERROR : No Destination Directory Specified.
12440정성태12/4/20208848오류 유형: 689. SignTool Error: Invalid option: /as
12439정성태12/4/202010098디버깅 기술: 176. windbg - 특정 Win32 API에서 BP가 안 걸리는 경우 (2) [1]
12438정성태12/2/202010053오류 유형: 688. .Visual C++ - Error C2011 'sockaddr': 'struct' type redefinition
12437정성태12/1/20209710VS.NET IDE: 155. pfx의 암호 키 파일을 Visual Studio 없이 등록하는 방법
12436정성태12/1/20209995오류 유형: 687. .NET Core 2.2 빌드 - error MSB4018: The "RazorTagHelper" task failed unexpectedly.
12435정성태12/1/202015445Windows: 181. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (4) - ReuseUnicastPort를 이용한 포트 고갈 문제 해결 [1]파일 다운로드1
12434정성태11/30/202010770Windows: 180. C# - dynamicport 값의 범위를 알아내는 방법
12433정성태11/29/20209815Windows: 179. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (3) - SO_PORT_SCALABILITY파일 다운로드1
12432정성태11/29/202011228Windows: 178. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (2) - SO_REUSEADDR [1]파일 다운로드1
12431정성태11/27/20209160.NET Framework: 976. UnmanagedCallersOnly + C# 9.0 함수 포인터 사용 시 x86 빌드에서 오동작하는 문제파일 다운로드1
12430정성태11/27/20209900오류 유형: 686. Ubuntu - E: The repository 'cdrom://...' does not have a Release file.
... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...