여기에서 확인한 것과 체크하지 않은 것의 차이점은 실제로 일리노이 버그 나 일부 나쁜 소스 코드입니다. (저는 언어 전문가가 아니므로 C# 컴파일러가 올바른 코드를 생성하는지 논평하지 않겠습니다. 일리노이주의 모호한 소스 코드). 나는 4.0.30319.1 버전의 C# 컴파일러를 사용하여이 테스트 코드를 컴파일했다 (2.0 버전은 같은 일을하는 것처럼 보였지만). 필자가 사용하는 명령 줄 옵션은/o +/unsafe/debug : pdbonly입니다. 선택되지 않은 블록
, 우리는이 IL 코드를 가지고 IL에서
//000008: unchecked
//000009: {
//000010: Console.WriteLine("{0:x}", (long)(testPtr + offset));
IL_000a: ldstr "{0:x}"
IL_000f: ldloc.0
IL_0010: ldloc.1
IL_0011: add
IL_0012: conv.u8
IL_0013: box [mscorlib]System.Int64
IL_0018: call void [mscorlib]System.Console::WriteLine(string,
object)
11 오프셋을 추가 2 피연산자 입력 바이트 * 중 하나 형 UINT32 다른을 얻는다. CLI 스펙에 따라 이들은 각각 고유 한 int 및 int32로 실제로 표준화됩니다. CLI 스펙 (세분화 된 파티션 III)에 따르면 결과는 기본 int입니다. 따라서 secodn 피연산자는 native int 유형으로 승격되어야합니다. 명세에 따르면, 이것은 부호 연장을 통해 달성된다. 따라서 uint.MaxValue (부호있는 표기법에서 0xFFFFFFFF 또는 -1)는 부호가 0xFFFFFFFFFFFFFFFF까지 확장됩니다. 그런 다음 2 개의 피연산자가 추가됩니다 (0x0000000008000000L + (-1L) = 0x0000000007FFFFFFL). conv opcode는 네이티브 int를 생성 된 코드에서 nop 인 int64로 변환하기위한 검증 목적으로 만 필요합니다. 그것은 추가 및 전환 오피 코드를 제외하고 거의 동일하다
//000012: checked
//000013: {
//000014: Console.WriteLine("{0:x}", (long)(testPtr + offset));
IL_001d: ldstr "{0:x}"
IL_0022: ldloc.0
IL_0023: ldloc.1
IL_0024: add.ovf.un
IL_0025: conv.ovf.i8.un
IL_0026: box [mscorlib]System.Int64
IL_002b: call void [mscorlib]System.Console::WriteLine(string,
object)
:
지금 체크 블록, 우리는이 IL있다. 추가 연산 코드에 2 개의 '접미어'를 추가했습니다. 첫 번째 것은 ".ovf"접미사로 명백한 의미가 있습니다. 오버플로가 있는지 확인하지만 두 번째 접미사 인 ".un"을 활성화해야합니다. (즉 "add.un"이없고 "add.ovf.un"만 있습니다). ".un"에는 2 가지 효과가 있습니다.가장 명백한 것은 오퍼랜드가 부호없는 정수인 것처럼 추가 및 오버플로 검사가 수행된다는 것입니다. 우리 CS 클래스에서 돌아 왔을 때, 2의 보수 바이너리 인코딩 덕분에 서명 된 덧셈과 부호없는 덧셈은 동일하므로 ".un"은 실제로 오버플로 검사에만 영향을 미칩니다. 맞습니까?
틀린.
IL 스택에는 2 개의 64 비트 숫자가 없다는 것을 기억하십시오. 우리는 int32와 네이티브 int (정규화 이후)를 가지고 있습니다. ".un"은 int32에서 native 로의 변환이 위와 같이 기본 "conv.i"보다는 "conv.u"와 같이 처리된다는 것을 의미합니다. 따라서 uint.MaxValue는 0x00000000FFFFFFFFL까지 0으로 확장됩니다. 그런 다음 올바르게 추가하면 0x0000000107FFFFFFL이 생성됩니다. conv opcode는 부호없는 피연산자가 signed int64로 표현 될 수 있는지 확인합니다.
수정 된 내용은 64 비트 용입니다. IL 수준에서 uint32 피연산자를 네이티브 int 또는 부호없는 네이티브 int로 명시 적으로 변환 한 다음 32 비트 및 64 비트 모두 check 및 unchecked가 동일하게 수정됩니다.
일부 코드를 보여줄 수 있습니까? – aL3891
오프셋 서명 및 포인터 부호 없음? 음의 오프셋이 거의 필요하지 않습니다. –
코드의 관련 부분은 다음과 같습니다. this.currentLocation + = sizeof (byte), this.currentLocation + = numberOfChildren * sizeof (ushort), this.currentLocation + = subTree.Length 등 음수 오프셋이 없습니다. –