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 바이트 걸린다. 모두의 값에 대한 메모리 그래서 RefType
및 ValType
다음과 같다 :
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 ]
이것은 힙에있는 객체에 대한 string
4 개의 참조를 포함 하는 배열입니다 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 의 값 은 short
2 바이트 숫자입니다.
값 유형이 저장되는 위치는 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;
답변
답변
정수 배열은 힙에 할당됩니다. myIntegers는 정수가 할당 된 섹션의 시작을 나타냅니다. 해당 참조는 스택에 있습니다.
스택에있는 객체 유형과 같은 참조 유형 객체의 배열이있는 경우 myObjects []는 객체 자체를 참조하는 여러 값을 참조합니다.
요약하면, myIntegers를 일부 함수에 전달하면 실제 정수 무리가 할당되는 위치에만 참조를 전달합니다.
답변
예제 코드에는 권투가 없습니다.
값 유형은 정수 배열과 마찬가지로 힙에 존재할 수 있습니다. 배열은 힙에 할당되고 값 유형 인 int를 저장합니다. 배열의 내용은 default (int)로 초기화되며 이는 0입니다.
값 유형이 포함 된 클래스를 고려하십시오.
class HasAnInt
{
int i;
}
HasAnInt h = new HasAnInt();
변수 h는 힙에있는 HasAnInt 인스턴스를 나타냅니다. 값 유형이 포함되어 있습니다. 그것은 완벽하게 괜찮습니다. ‘i’는 수업에 포함되어 있기 때문에 힙에 살았습니다. 이 예에는 권투도 없습니다.