2010-07-26 1 views
3

한 줄을 주석으로 처리하여 아래에 코드 블록이 있습니다. CreateArray 메서드에서 일어나는 일은 주석 처리 된 행과 동일한 작업입니다. 제 질문은 라인 b->ArrayItems = d의 주석 처리가 해제되었지만 주석 처리 될 때 가비지를 반환 할 때 작동하는 이유는 무엇입니까? 모든 정보가 관리되지 않기 때문에 나는 "고정"해야한다고 생각하지 않습니다. 이 가정은 틀린가?안전하지 않은 코드 및 스택 alloc을 사용할지 여부에 대한 혼란

class Program 
{ 
    unsafe static void Main(string[] args) 
    { 
     someInstance* b = stackalloc someInstance[1]; 
     someInstance* d = stackalloc someInstance[8]; 

     b->CreateArray(); 
//  b->ArrayItems = d; 

     *(b->ArrayItems)++ = new someInstance() { IntConstant = 5 }; 
     *(b->ArrayItems)++ = new someInstance() { IntConstant = 6 }; 

     Console.WriteLine((b)->ArrayItems->IntConstant); 
     Console.WriteLine(((b)->ArrayItems - 1)->IntConstant); 
     Console.WriteLine(((b)->ArrayItems - 2)->IntConstant); 
     Console.Read(); 
    } 
} 

public unsafe struct someInstance 
{ 
    public someInstance* ArrayItems; 
    public int IntConstant; 
    public void CreateArray() 
    { 
     someInstance* d = stackalloc someInstance[8]; 
     ArrayItems = d; 
    } 
} 

답변

12

내 질문은 라인이 주석 때 작동하지만 주석 때 쓰레기를 반환 않는 이유입니다.

주석으로 라인 마스킹 CreateArray 의한 버그되는 것이다. 주석 처리하면 버그가 노출됩니다. 그러나 버그는 거기에 상관없이 존재합니다.

는 명세서 분명히 명시 같이 기능 부재의 실행 동안 생성

모든 할당 된 스택 메모리 블록이 때 자동으로 그 기능 부재 반환 폐기된다.

CreateArray 함수는 블록을 할당하고 블록에 대한 포인터를 저장하며 블록이 삭제되고 이제는 가비지 블록에 대한 포인터가 생깁니다. 이 필요합니다. ~ 은 블록이 무효화 된 후에 저장소에 액세스 할 수 있도록 스택 블록의 블록에 대한 포인터를 절대로 저장하지 않습니다. 힙은 참조를 저장해야 할 경우 블록을 할당하고 완료되면 블록 할당을 기억하십시오.

안전하지 않은 코드 에서 을 완전히 이해하고 관리되는 메모리 모델에 대해이 필요합니다.모두입니다. 관리되는 메모리에 대한 모든 것을 이해하지 못하는 경우 안전하지 않은 코드를 작성하지 마십시오.

그렇다면 "언제 포인터를 얻기 위해 메모리를 고쳐야합니까?"라는 더 큰 혼란이있는 것 같습니다. 대답은 간단합니다. 메모리가 이동식 메모리 인 경우에만 메모리를 수정해야합니다. 고정은 이동 가능한 메모리를 움직이지 않는 메모리로 변환합니다. 그게 뭔지는입니다.

움직일 수없는 것의 주소 만 가져올 수 있습니다. 움직일 수있는 무언가의 주소를 가져 가면 이동하면 분명히 주소가 틀립니다. 주소를 가져 오기 전에 메모리가 이동하지 않도록해야하며 주소를 다시 이동할 수있게 한 후에 주소를 사용하지 않아야합니다.

+0

그건 함정입니다. 놀기 시작하고 실수를하지 않으면, 관리 메모리 모델의 모든 것을 이해하는 법을 배우지 못할 것입니다.하지만 안전하지 못한 게임을하기 전에 모든 것을 먼저 이해해야 만한다면 ... – Abel

+0

" 틀림없이 조금 강한가? 스택이 할당 된 블록보다 스토리지가 더 이상 지속되지 않는 한 확실히 저장할 수 있습니다. –

+1

@Marc : 물론, 나는 당신의 요점을 알고 있습니다. "저장"이란 말은 현재 범위를 벗어나는 * 지속적인 * 것을 의미합니다. –

1

Stackalloc은 콜 스택에 약간의 공간을 할당합니다. 그러면 현재 레벨의 컨텍스트 (예 : 메소드 남기기)에서 벗어날 때 공간이 손실됩니다. 문제는 stackalloc이 메소드 내에있을 때 스택의 해당 영역이 더 이상 당신이 그 메소드를 떠날 때 가지고 놀지 않는다는 것입니다.

그래서, 당신이 할 경우이 :

foo() 
{ 
    stuff = stackalloc byte[1] 
    Do something with stuff 
} 

당신이 의미, 다시 감겨 스택을 foo를 떠나 일단 "물건"foo는 내부에만 유효합니다 당신이 할 경우 :

foo() 
{ 
    byte* allocate() 
    { 
     return stackalloc[1] 
    } 

    stuff = allocate() 
    do something with stuff 
} 

allocate 메서드를 종료 할 때 allocate의 반환 값은 쓰레기가됩니다. 즉, "stuff"는 결코 의미가 없습니다.

1

귀하의 가정은 부분적으로 정확하지만 잘못 이해되었습니다. 여기에 인용 from this MSDN page입니다 :

안전 모드에서, 당신은 가비지 컬렉션에 적용되지 않습니다 및 따라서 고정 할 필요가 없습니다 스택에 메모리를 할당 할 수 있습니다. 자세한 내용은 stackalloc을 참조하십시오.

일부 문은 자동으로 스택에 변수를 할당합니다. 즉, 메소드 내부의 값 유형을 지정하며, 나머지는 특별히 stackalloc을 사용하여 지정해야합니다.

스택 할당 메모리는 메소드가 끝난 후 폐기되므로 문제가됩니다 (Eric Lipperts의 대답, 누가 나에게 먼저 썼는지보십시오).