[C#] 배열, 힙 및 스택 및 값 유형

int[] myIntegers;
myIntegers = new int[100];

위의 코드에서 새로운 int [100]가 힙에서 배열을 생성합니까? C #을 통해 CLR에서 읽은 내용에서 대답은 그렇습니다. 그러나 내가 이해할 수없는 것은 배열 내부의 실제 int에 발생하는 것입니다. 그것들은 값 유형이기 때문에 예를 들어 myIntegers를 프로그램의 다른 부분에 전달할 수 있고 항상 스택에 남아 있으면 스택을 혼란스럽게 만들 수 있기 때문에 상자에 넣어야한다고 생각합니다. . 아니면 내가 틀렸어? 나는 그들이 박스에 담겨 있고 배열이 존재하는 한 힙에 살 것이라고 생각합니다.



답변

배열은 힙에 할당되며 int는 상자로 표시되지 않습니다.

사람들이 참조 유형이 힙에 할당되고 값 유형이 스택에 할당되었다고 말했기 때문에 혼란의 원인이 될 수 있습니다. 이것은 완전히 정확한 표현이 아닙니다.

모든 지역 변수 및 매개 변수는 스택에 할당됩니다. 여기에는 값 형식과 참조 형식이 모두 포함됩니다. 이 둘의 차이점 은 변수에 저장된 것입니다 . 놀랍게도, 값 유형 의 경우 유형 이 변수에 직접 저장되고 참조 유형의 경우 유형 값이 힙에 저장되며이 값에 대한 참조 는 변수에 저장됩니다.

필드도 마찬가지입니다. 집계 유형 (a class또는 a struct) 의 인스턴스에 메모리가 할당되면 각 인스턴스 필드에 대한 스토리지를 포함해야합니다. 참조 유형 필드의 경우이 스토리지는 값에 대한 참조 만 보유하며 나중에 힙에 할당됩니다. 값 유형 필드의 경우이 스토리지는 실제 값을 보유합니다.

따라서 다음 유형이 주어집니다.

class RefType{
    public int    I;
    public string S;
    public long   L;
}

struct ValType{
    public int    I;
    public string S;
    public long   L;
}

이러한 각 유형의 값에는 16 바이트의 메모리가 필요합니다 (32 비트 워드 크기로 가정). 필드 I각각의 경우에 그 값을 저장하기 위해 4 바이트를 얻어,이 필드는 S그 기준을 저장하는 4 바이트를 취하고,이 필드는 L그 값을 저장하기 위해 8 바이트 걸린다. 모두의 값에 대한 메모리 그래서 RefTypeValType다음과 같다 :

 0 ┌────────────────────┐
   │ 나 │
 4 ├────────────────────┤
   │ S │
 8 ├────────────────────┤
   │ L │
   │ │
16 └────────────────────┘

지금 당신은 유형의 함수에 세 개의 지역 변수가 있다면 RefType, ValType그리고 int[]다음과 같이 :

RefType refType;
ValType valType;
int[]   intArray;

그러면 스택은 다음과 같습니다.

 0 ┌────────────────────┐
   │ RefType │
 4 ├────────────────────┤
   │ valType │
   │ │
   │ │
   │ │
20 ├────────────────────┤
   │ intArray │
24 └────────────────────┘

다음과 같이 이러한 로컬 변수에 값을 할당 한 경우 :

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

그런 다음 스택이 다음과 같이 보일 수 있습니다.

 0 ┌────────────────────┐
   │ 0x4A963B68 │-`refType`의 힙 주소
 4 ├────────────────────┤
   │ 200 │-valval.I의 값
   │ 0x4A984C10 │-`valType.S`의 힙 주소
   │ 0x44556677 │-32 비트의 낮은 valval.L
   │ 0x00112233 │-높은 32 비트`valType.L`
20 ├────────────────────┤
   │ 0x4AA4C288 │-`intArray`의 힙 주소
24 └────────────────────┘

주소의 메모리 0x4A963B68(의 값 refType)는 다음과 같습니다.

 0 ┌────────────────────┐
   │ 100 │-`refType.I '의 값
 4 ├────────────────────┤
   │ 0x4A984D88 │-`refType.S`의 힙 주소
 8 ├────────────────────┤
   │ 0x89ABCDEF │-32 비트의 낮은 RefType.L
   │ 0x01234567 │-높은 32 비트`refType.L`
16 └────────────────────┘

주소의 메모리 0x4AA4C288(의 값 intArray)는 다음과 같습니다.

 0 ┌────────────────────┐
   │ 4 │-배열의 길이
 4 ├────────────────────┤
   │ 300 │-`intArray [0]`
 8 ├────────────────────┤
   │ 301 │-`intArray [1]`
12 ├────────────────────┤
   │ 302 │-`intArray [2]`
16 ├────────────────────┤
   │ 303 │-`intArray [3]`
20 └────────────────────┘

이제 intArray다른 함수에 전달 하면 스택에 푸시 된 값 0x4AA4C288은 배열 의 복사본이 아닌 배열의 주소입니다 .


답변

예, 어레이는 힙에 위치합니다.

배열 내부의 정수는 박스로 표시되지 않습니다. 힙에 값 유형이 존재한다고해서 반드시 상자에 넣는 것은 아닙니다. 복싱은 int와 같은 값 유형이 유형 객체의 참조에 지정된 경우에만 발생합니다.

예를 들어

상자에 들어 있지 않습니다 :

int i = 42;
myIntegers[0] = 42;

박스 :

object i = 42;
object[] arr = new object[10];  // no boxing here 
arr[0] = 42;

이 주제에 대한 Eric의 게시물을 확인할 수도 있습니다.


답변

무슨 일이 일어나고 있는지 이해하려면 몇 가지 사실이 있습니다.

  • 객체는 항상 힙에 할당됩니다.
  • 힙에는 객체 만 포함됩니다.
  • 값 유형은 스택에 할당되거나 힙에있는 객체의 일부입니다.
  • 배열은 객체입니다.
  • 배열은 값 유형 만 포함 할 수 있습니다.
  • 객체 참조는 값 유형입니다.

따라서 정수 배열이있는 경우 배열은 힙에 할당되고 여기에 포함 된 정수는 힙에서 배열 오브젝트의 일부입니다. 정수는 개별 객체가 아닌 힙의 배열 객체 내부에 상주하므로 박스로 표시되지 않습니다.

문자열 배열이 있으면 실제로 문자열 참조 배열입니다. 참조는 값 유형이므로 힙에서 배열 객체의 일부가됩니다. 배열에 문자열 객체를 넣는 경우 실제로는 배열에 문자열 객체에 대한 참조를 넣고 문자열은 힙에서 별도의 객체입니다.


답변

귀하의 질문의 핵심은 참조 및 가치 유형에 대한 오해라고 생각합니다. 이것은 아마도 모든 .NET 및 Java 개발자가 어려움을 겪고있는 것입니다.

배열은 값 목록 일뿐입니다. 참조 유형의 배열 (예 : a string[]) 인 string경우 참조는 참조 유형 의 이므로 힙의 다양한 객체에 대한 참조 목록입니다 . 내부적으로 이러한 참조는 메모리의 주소에 대한 포인터로 구현됩니다. 이것을 시각화하려면 그러한 배열은 메모리 (힙)에서 다음과 같이 보입니다.

[ 00000000, 00000000, 00000000, F8AB56AA ]

이것은 힙에있는 객체에 대한 string4 개의 참조를 포함 하는 배열입니다 string(여기서 숫자는 16 진수 임). 현재 마지막은 string실제로 무엇이든 가리 킵니다 (메모리는 할당 될 때 모두 0으로 초기화됩니다).이 배열은 기본적으로 C # 에서이 코드의 결과입니다.

string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR

위의 배열은 32 비트 프로그램에 있습니다. 64 비트 프로그램에서 참조 (큰 배 될 F8AB56AA것입니다 00000000F8AB56AA).

당신이 값 형식의 배열이있는 경우 (AN 말 int[]은 AS 다음 배열은 정수의 목록입니다) 값 형식의가 있다 값 자체 (따라서 이름). 이러한 배열의 시각화는 다음과 같습니다.

[ 00000000, 45FF32BB, 00000000, 00000000 ]

이것은 4 개의 정수 배열입니다. 두 번째 int 만 값 (1174352571, 즉 16 진수의 십진수 표시)이 할당되고 나머지 정수는 0이됩니다 (내가 말했듯이 메모리는 0으로 초기화됩니다) 16 진수로 00000000은 10 진수로 0입니다. 이 배열을 생성 한 코드는 다음과 같습니다.

 int[] integers = new int[4];
 integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too

int[]배열은 힙에도 저장됩니다.

다른 예로 short[4]배열 의 메모리 는 다음과 같습니다.

[ 0000, 0000, 0000, 0000 ]

a 의 short2 바이트 숫자입니다.

값 유형이 저장되는 위치는 Eric Lippert 가 값과 참조 유형의 차이 (행동의 차이)에 고유 한 것이 아니라 Eric Lippert가 여기서 잘 설명하는 구현 세부 사항 일뿐 입니다.

메소드에 무언가를 전달하면 (참조 유형 또는 값 유형) , 해당 유형 의 사본 이 실제로 메소드에 전달됩니다. 참조 유형의 경우 은 참조이며 (구현 세부 사항이지만 메모리 조각에 대한 포인터로 생각하십시오) 값 유형의 경우 값 자체가 문제입니다.

// Calling this method creates a copy of the *reference* to the string
// and a copy of the int itself, so copies of the *values*
void SomeMethod(string s, int i){}

복싱 은 값 유형을 참조 유형으로 변환 한 경우에만 발생합니다 . 이 코드 박스 :

object o = 5;


답변

위의 답변은 @P Daddy의 그림입니다.

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

그리고 나는 내 스타일로 해당 내용을 설명했습니다.

여기에 이미지 설명을 입력하십시오


답변

정수 배열은 힙에 할당됩니다. myIntegers는 정수가 할당 된 섹션의 시작을 나타냅니다. 해당 참조는 스택에 있습니다.

스택에있는 객체 유형과 같은 참조 유형 객체의 배열이있는 경우 myObjects []는 객체 자체를 참조하는 여러 값을 참조합니다.

요약하면, myIntegers를 일부 함수에 전달하면 실제 정수 무리가 할당되는 위치에만 참조를 전달합니다.


답변

예제 코드에는 권투가 없습니다.

값 유형은 정수 배열과 마찬가지로 힙에 존재할 수 있습니다. 배열은 힙에 할당되고 값 유형 인 int를 저장합니다. 배열의 내용은 default (int)로 초기화되며 이는 0입니다.

값 유형이 포함 된 클래스를 고려하십시오.


    class HasAnInt
    {
        int i;
    }

    HasAnInt h = new HasAnInt();

변수 h는 힙에있는 HasAnInt 인스턴스를 나타냅니다. 값 유형이 포함되어 있습니다. 그것은 완벽하게 괜찮습니다. ‘i’는 수업에 포함되어 있기 때문에 힙에 살았습니다. 이 예에는 권투도 없습니다.