값에 관계없이 유형이 항상 특정 크기 인 이유는 무엇입니까?


149

구현은 실제 크기 유형에 따라 다를 수 있지만 대부분 부호없는 int 및 float와 같은 유형은 항상 4 바이트입니다. 그러나 왜 유형에 관계없이 값이 항상 특정 양의 메모리를 차지 합니까? 예를 들어 값이 255 인 다음 정수를 만든 경우

int myInt = 255;

그런 다음 myInt컴파일러에서 4 바이트를 차지합니다. 그러나 실제 값 255은 1 바이트로만 표현할 수 있습니다. 왜 myInt1 바이트의 메모리 만 차지하지 않습니까? 또는보다 일반적인 방법은 다음과 같습니다. 값을 나타내는 데 필요한 공간이 해당 크기보다 작을 때 왜 유형에 하나의 크기 만 연결되어 있습니까?


15
1) " 그러나 실제 값 256은 1 바이트로만 표현 될 수 있습니다 " 1 바이트unsinged 로 표현 될 수있는 가장 큰 값 은 잘못되었습니다 255. 2) 값이 변함에 따라 변수의 최적 저장 크기 계산 및 저장 영역 축소 / 확장의 오버 헤드를 고려하십시오.
Algirdas Preidžius

99
글쎄, 메모리에서 값 을 읽을 때가 되면 머신이 읽을 바이트 수를 어떻게 결정할지 제안 하는가? 기계는 값 읽기를 중지 할 위치를 어떻게 알 수 있습니까? 추가 시설이 필요합니다. 그리고 일반적으로 이러한 추가 기능에 대한 메모리 및 성능 오버 헤드는 단순히 고정 된 4 바이트 unsigned int값을 사용하는 경우보다 훨씬 높습니다 .
AnT

74
나는이 질문을 정말로 좋아한다. 대답하기가 간단 해 보이지만 정확한 설명을하려면 컴퓨터와 컴퓨터 아키텍처가 실제로 어떻게 작동하는지 잘 이해해야합니다. 대부분의 사람들은 아마도 그것에 대한 포괄적 인 설명없이 당연한 것으로 받아 들일 것입니다.
andreee

37
변수의 값에 1을 더해 256을 만들면 어떻게되는지 생각해 보면 확장해야합니다. 어디로 확장됩니까? 공간을 확보하기 위해 나머지 메모리를 이동합니까? 변수 자체가 이동합니까? 그렇다면, 어디로 이동하고 업데이트해야하는 포인터를 어떻게 찾습니까?
molbdnilo 2016 년

13
@ someidiot 아니, 당신은 틀렸다. std::vector<X>항상 같은 크기, 즉 sizeof(std::vector<X>)컴파일 타임 상수입니다.
SergeyA

답변:


131

컴파일러는 일부 기계에 대해 어셈블러 (및 궁극적으로 기계 코드)를 생성해야하며 일반적으로 C ++은 해당 기계에 공감하려고합니다.

기본 머신에 공감한다는 것은 대충 의미합니다. 머신이 빠르게 실행할 수있는 작업에 효율적으로 매핑되는 C ++ 코드를 쉽게 작성할 수 있습니다. 따라서 하드웨어 플랫폼에서 빠르고 "자연스러운"데이터 유형 및 작업에 대한 액세스를 제공하고자합니다.

구체적으로 특정 기계 아키텍처를 고려하십시오. 현재 Intel x86 제품군을 살펴 보겠습니다.

인텔 ® 64 및 IA-32 아키텍처 소프트웨어 개발자 설명서 vol 1 ( 링크 ), 섹션 3.4.1은 다음과 같이 말합니다.

32 비트 범용 레지스터 EAX, EBX, ECX, EDX, ESI, EDI, EBP 및 ESP는 다음 항목을 보유하기 위해 제공됩니다.

• 논리 및 산술 연산을위한 피연산자

주소 계산을위한 피연산자

메모리 포인터

따라서 간단한 C ++ 정수 산술을 컴파일 할 때 컴파일러가 이러한 EAX, EBX 등 레지스터를 사용하기를 원합니다. 이것은 내가 선언 할 때이 int레지스터와 호환되는 것이어야하므로 효율적으로 사용할 수 있습니다.

레지스터는 항상 같은 크기 (여기서는 32 비트)이므로 int변수는 항상 32 비트입니다. 변수 값을 레지스터에로드하거나 레지스터를 변수에 다시 저장할 때마다 변환 할 필요가 없도록 동일한 레이아웃 (little-endian)을 사용합니다.

godbolt 를 사용 하면 컴파일러가 간단한 코드에 대해 수행하는 작업을 정확하게 볼 수 있습니다.

int square(int num) {
    return num * num;
}

GCC 8.1과 -fomit-frame-pointer -O3단순성을 위해 다음 과 같이 컴파일합니다 .

square(int):
  imul edi, edi
  mov eax, edi
  ret

이것은 다음을 의미합니다.

  1. int num매개 변수는 레지스터 EDI에 전달되었습니다. 이는 인텔이 기본 레지스터에 대해 예상하는 크기와 레이아웃임을 의미합니다. 이 함수는 아무것도 변환 할 필요가 없습니다
  2. 곱셈은 ​​단일 명령어 ( imul)이며 매우 빠릅니다.
  3. 결과를 반환하는 것은 단순히 다른 레지스터에 복사하는 문제입니다 (호출자는 결과가 EAX에 입력 될 것으로 예상 함)

편집 : 기본 레이아웃이 아닌 레이아웃을 사용하여 차이점을 표시하기 위해 관련 비교를 추가 할 수 있습니다. 가장 간단한 경우는 기본 너비 이외의 값을 저장하는 것입니다.

Godbolt를 다시 사용 하여 간단한 기본 곱셈을 비교할 수 있습니다.

unsigned mult (unsigned x, unsigned y)
{
    return x*y;
}

mult(unsigned int, unsigned int):
  mov eax, edi
  imul eax, esi
  ret

비표준 너비에 해당하는 코드

struct pair {
    unsigned x : 31;
    unsigned y : 31;
};

unsigned mult (pair p)
{
    return p.x*p.y;
}

mult(pair):
  mov eax, edi
  shr rdi, 32
  and eax, 2147483647
  and edi, 2147483647
  imul eax, edi
  ret

모든 추가 명령어는 입력 형식 (두 개의 31 비트 부호없는 정수)을 프로세서가 기본적으로 처리 할 수있는 형식으로 변환하는 것과 관련이 있습니다. 결과를 다시 31 비트 값으로 저장하려면이 작업을 수행하는 또 하나의 지침이있을 것입니다.

이러한 추가 복잡성은 공간 절약이 매우 중요한 경우에만 귀찮게 할 수 있음을 의미합니다. 이 경우 네이티브 unsigned또는 uint32_t유형 을 사용하는 것과 비교하여 훨씬 간단한 코드를 생성하는 것 보다 두 비트 만 절약 합니다.


동적 크기에 대한 참고 사항 :

위의 예는 여전히 가변 너비가 아닌 고정 너비 값이지만 너비 (및 정렬)는 더 이상 기본 레지스터와 일치하지 않습니다.

x86 플랫폼에는 기본 32 비트 외에도 8 비트 및 16 비트를 포함한 여러 가지 기본 크기가 있습니다 (64 비트 모드 및 기타 여러 가지 단순함을 염두에두고 있습니다).

이러한 유형 (char, int8_t, uint8_t, int16_t 등) 아키텍처에서 직접 지원되며 부분적으로 이전 8086 / 286 / 386 / etc와의 하위 호환성을 위해 사용됩니다. 명령 세트 등.

충분하고, 실용적이 될 수있는 가장 작은 자연적인 고정 크기 유형 을 선택하는 것은 분명한 경우입니다. 여전히 빠르고 단일 명령어로드 및 저장이 가능하며 여전히 전속 네이티브 산술을 얻거나 성능을 향상시킬 수도 있습니다. 캐시 미스 감소.

이것은 가변 길이 인코딩과는 매우 다릅니다.이 중 일부와 함께 작업했으며 끔찍합니다. 모든로드는 단일 명령어 대신 루프가됩니다. 모든 상점은 또한 루프입니다. 모든 구조는 가변 길이이므로 배열을 자연스럽게 사용할 수 없습니다.


효율성에 대한 추가 참고 사항

이후의 의견에서는 스토리지 크기와 관련하여 "효율적"이라는 단어를 사용했습니다. 스토리지 크기를 최소화하기로 선택하는 경우도 있습니다. 파일에 많은 수의 값을 저장하거나 네트워크를 통해 전송할 때 중요 할 수 있습니다. 트레이드 오프는 그 값을 레지스터에로드하여 값을 처리 해야 하며 변환을 수행하는 것이 자유롭지 않다는 것입니다.

효율성에 대해 논의 할 때, 우리가 최적화하고있는 것이 무엇인지, 그리고 트레이드 오프가 무엇인지 알아야합니다. 비원시 스토리지 유형을 사용하는 것은 공간 처리 속도를 교환하는 한 가지 방법이며 때로는 의미가 있습니다. 가변 길이 저장소 (산술 유형 의 경우)를 사용하면 공간을 최소로 절약하기 위해 더 많은 처리 속도 (및 코드 복잡성 및 개발자 시간)를 제공합니다.

이 비용을 지불하면 대역폭이나 장기 저장소를 절대적으로 최소화해야 할 때만 가치가 있으며, 이러한 경우 일반적으로 단순하고 자연스러운 형식을 사용하는 것이 더 쉽고 범용 시스템으로 압축하는 것이 더 쉽습니다. (zip, gzip, bzip2, xy 등)


tl; dr

각 플랫폼에는 하나의 아키텍처가 있지만 데이터를 표현하는 다양한 방법을 기본적으로 무제한으로 만들 수 있습니다. 모든 언어가 무제한의 내장 데이터 유형을 제공하는 것은 합리적이지 않습니다. 따라서 C ++은 플랫폼의 고유 한 자연 데이터 유형 세트에 대한 암시 적 액세스를 제공하며 다른 (네이티브가 아닌) 표현을 직접 코딩 할 수 있습니다.


나는 그 모든 것을 이해하려고 노력하면서 멋진 답변을 모두보고 있습니다. 따라서 귀하의 답변과 관련하여, 레지스터의 더 많은 변수를 허용하지 않고 정수의 경우 32 비트 미만의 동적 크기는 아닙니다 ? 엔디안이 동일하다면 왜 이것이 최적이 아닐까요?
Nichlas Uden 2016 년

7
@asd 그러나 현재 얼마나 많은 변수가 레지스터에 저장되어 있는지 알아내는 코드에서 몇 개의 레지스터를 사용할 것입니까?
user253751

1
FWIW 일반적으로 공간 절약이 포장 및 포장 풀기 속도보다 중요하다고 판단되는 경우 사용 가능한 가장 작은 공간에 여러 값을 포장하는 것이 일반적입니다. 프로세서는 내장 레지스터 이외의 다른 것에서 올바르게 산술을 수행하는 방법을 알지 못하기 때문에 일반적으로 포장 형태로 자연스럽게 처리 할 수 ​​없습니다. 프로세서가 지원되는 부분 예외에 대한 BCD를 찾으십시오
쓸모없는

3
실제로하면 않는 일부 값에 대한 모든 32 개 비트를 필요로, 나는 여전히 지금은 필요 길이를 저장하는 장소가 필요할 이상의 경우 32 비트.
쓸모없는

1
+1. "단순하고 자연스러운 형식과 압축"에 대한 메모는 일반적으로 더 좋습니다 : 이것은 일반적으로 사실 이지만 , 일부 데이터의 경우 VLQ- 각-값-그러면 압축-전체-압축은 압축-보다 전체 데이터 및 일부 응용 프로그램의 경우 데이터가 서로 다르거 나 ( git'메타 데이터 에서와 같이 ') 실제로 메모리에 가끔씩 무작위로 액세스하거나 수정해야 할 필요가 있기 때문에 데이터를 함께 압축 할 수 없습니다. HTML + CSS 렌더링 엔진에서와 같이 값을 사용하므로 VLQ와 같은 것을 사용하여 적절한 위치에서만이 기능을 사용할 수 있습니다.
mtraceur

139

유형은 기본적으로 스토리지를 나타내며 현재 값이 아니라 보유 할 수있는 최대 값으로 정의됩니다 .

아주 간단한 비유는 집이 될 것입니다-집은 얼마나 많은 사람들이 살고 있는지에 관계없이 크기가 고정되어 있으며 특정 크기의 집에 살 수있는 사람들의 최대 수를 규정하는 건물 코드도 있습니다.

그러나 한 사람이 10 명을 수용 할 수있는 집에 살더라도 집의 크기는 현재 거주자 수에 영향을받지 않습니다.


31
나는 유추를 좋아한다. 약간 확장하면 유형에 고정 메모리 크기를 사용하지 않는 프로그래밍 언어를 사용한다고 가정 할 수 있으며, 사용하지 않을 때마다 집안의 방을 쓰러 뜨리고 필요할 때 다시 작성하는 것과 비슷합니다. (예를 들어 집을 짓고 필요할 때 그대로 둘 수있을 때 엄청난 오버 헤드).
ahouse101

5
"유형은 기본적으로 스토리지를 나타 내기 때문에" 이것은 모든 언어에 적용되는 것은 아닙니다 (예 : typescript)
corvus_192

56
@ corvus_192 태그는 의미가 있습니다. 이 질문은 'typescript'가 아닌 C ++로 태그되었습니다
SergeyA

4
실제로, 무제한 정밀도 정수를 가진 많은 언어가 있으며 필요에 따라 확장됩니다. 이러한 언어는 변수에 고정 메모리를 할당 할 필요가 없으며 내부적으로 객체 참조로 구현됩니다. 예 : Lisp, Python.
Barmar

2
@jamesqf MP 연산이 자동 메모리 관리를 수행하는 Lisp에서 처음으로 채택 된 것은 아마도 coicidence가 아닙니다. 설계자들은 성능의 영향이 프로그래밍의 용이성에 부수적 인 요소라고 생각했습니다. 그리고 영향을 최소화하기 위해 최적화 기술이 개발되었습니다.
Barmar

44

최적화 및 단순화입니다.

고정 된 크기의 객체를 가질 수 있습니다. 따라서 값을 저장합니다.
또는 다양한 크기의 오브제를 사용할 수 있습니다. 그러나 가치와 크기를 저장합니다.

고정 크기의 객체

숫자를 조작하는 코드는 크기에 대해 걱정할 필요가 없습니다. 항상 4 바이트를 사용하고 코드를 매우 간단하게 만든다고 가정합니다.

동적 크기의 객체

숫자를 조작하는 코드는 변수를 읽을 때 값과 크기를 읽어야한다는 것을 이해해야합니다. 크기를 사용하여 레지스터에서 모든 높은 비트가 0으로 설정되도록합니다.

값이 현재 크기를 초과하지 않은 경우 값을 메모리에 다시 배치 할 경우 간단히 값을 메모리에 다시 배치하십시오. 그러나 값이 줄어들거나 커지면 개체의 저장 위치를 ​​메모리의 다른 위치로 이동하여 오버플로되지 않도록해야합니다. 이제 숫자의 위치를 ​​추적해야합니다 (크기에 비해 너무 커지면 움직일 수 있음). 또한 사용하지 않는 모든 변수 위치를 추적하여 잠재적으로 재사용 할 수 있도록해야합니다.

요약

고정 크기 객체에 대해 생성 된 코드는 훨씬 간단합니다.

노트

압축은 255가 1 바이트에 적합하다는 사실을 사용합니다. 다른 숫자에 대해 다른 크기 값을 능동적으로 사용하는 큰 데이터 세트를 저장하기위한 압축 체계가 있습니다. 그러나 이것은 실제 데이터가 아니기 때문에 위에서 설명한 복잡성이 없습니다. 저장을 위해 데이터를 압축 / 압축 해제하는 비용으로 더 적은 공간을 사용하여 데이터를 저장합니다.


4
이것은 나에게 가장 좋은 대답입니다. 어떻게 크기를 추적합니까? 와 메모리?
온라인 Thomas

@ThomasMoors 예, 정확히 : 더 많은 메모리가 있습니다. 예를 들어 동적 배열을 사용하는 경우 일부 int는 해당 배열에 요소 수를 저장합니다. 그 int자체는 다시 고정 된 크기를 갖습니다.
Alfe

1
@ThomasMoors 일반적으로 사용되는 두 가지 옵션이 있습니다. 둘 다 추가 메모리가 필요합니다. 데이터의 양을 알려주는 (고정 크기) 필드가 있습니다 (예 : 배열 크기의 정수 또는 첫 번째 위치의 "파스칼 스타일"문자열). element에는 문자 수가 포함되어 있거나, 또는 체인 (또는보다 복잡한 구조)을 가질 수 있습니다. 즉, 요소가 마지막 요소 (예 : 0으로 끝나는 문자열 또는 대부분의 형식의 링크 된 목록) 인 경우 메모 하는 체인 (또는 더 복잡한 구조)을 가질 수 있습니다 .
Peteris

27

C ++과 같은 언어에서 디자인 목표는 간단한 작업이 간단한 기계 명령어로 컴파일되는 것입니다.

모든 주류 CPU 명령어 세트는 고정 너비 유형에서 작동하며 가변 너비 유형을 수행하려면 여러 기계 명령어를 수행하여 처리해야합니다.

에 관해서는 이유 는 간단하고, 더 효율적 때문입니다 : 기본 컴퓨터 하드웨어가 그 방법 많은 경우 (전부는 아니지만).

컴퓨터를 테이프로 상상해보십시오.

| xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | ...

컴퓨터에서 테이프의 첫 번째 바이트를 보도록 지시 xx하면 유형이 거기서 멈추거나 다음 바이트로 진행되는지 여부를 어떻게 알 수 있습니까? 당신은 같은 수있는 경우 255(16 진수 FF) 또는 같은 숫자 65535(16 진수 FFFF) 첫 번째 바이트는 항상 FF.

그래서 어떻게 알지? 추가 논리를 추가하고 값이 다음 바이트로 계속됨을 나타 내기 위해 하나 이상의 비트 또는 바이트 값의 의미를 "오버로드"해야합니다. 이 논리는 결코 "무료"가 아닙니다. 소프트웨어에서 논리를 에뮬레이션하거나이를 위해 추가 트랜지스터를 CPU에 추가합니다.

C 및 C ++와 같은 고정 너비 유형의 언어가이를 반영합니다.

이러한 방식 일 필요 는 없으며 , 최대 효율적인 코드로의 매핑에 관심이없는보다 추상적 인 언어는 숫자 유형에 대해 가변 폭 인코딩 ( "가변 길이 수량"또는 VLQ라고도 함)을 자유롭게 사용할 수 있습니다.

추가 정보 : "가변 길이 수량"을 검색하면 해당 종류의 인코딩 실제로 효율적이고 추가 로직의 가치가있는 몇 가지 예를 찾을 수 있습니다 . 일반적으로 큰 범위 내의 어느 곳에 나있을 수있는 막대한 양의 값을 저장해야하지만 대부분의 값은 작은 하위 범위로 경향이 있습니다.


컴파일러가 코드를 손상시키지 않고 더 작은 공간에 값을 저장하여 벗어날 수 있음증명할 수 있다면 (예 : 단일 번역 단위 내에서만 내부적으로 볼 수있는 변수) 최적화 휴리스틱은 다음 같이 제안합니다. 대상 하드웨어에서 더 효율적일 것 입니다. 코드의 나머지 부분이 표준 작업을 수행하는 것처럼 "있는 것처럼"작동하는 한 그에 따라 하드웨어 최적화하여 더 적은 공간에 저장할 수 있습니다.

그러나 코드가 개별적으로 컴파일 될 수있는 다른 코드와 상호 운용 되어야하는 경우 크기는 일관성을 유지하거나 모든 코드가 동일한 규칙을 따르도록해야합니다.

일관성이 없으면 다음과 같은 합병증이 있습니다. int x = 255;코드가 있으면 나중에 어떻게해야 x = y합니까? int가변 폭일 수있는 경우 컴파일러는 필요한 최대 공간을 사전 할당하기 위해 미리 알고 있어야합니다. y별도의 컴파일 된 다른 코드 조각에서 인수가 전달 되면 어떻게됩니까?


26

Java는 "BigInteger"및 "BigDecimal"이라는 클래스를 사용하여 C ++의 GMP C ++ 클래스 인터페이스 (Digital Trauma 덕분에)와 마찬가지로이를 정확하게 수행합니다. 원한다면 거의 모든 언어로 쉽게 할 수 있습니다.

CPU는 항상 모든 길이의 작업을 지원하도록 설계된 BCD (Binary Coded Decimal) 를 사용할 수있었습니다. 그러나 오늘날의 GPU 표준에 따라 한 번에 한 바이트 씩 수동으로 작동하는 경향이 있습니다.

이러한 솔루션이나 다른 유사한 솔루션을 사용하지 않는 이유는 무엇입니까? 공연. 성능이 가장 우수한 언어는 엄격한 루프 작업 중에 변수를 확장 할 여유가 없으므로 결정적이지 않습니다.

대량 저장 및 운송 상황에서 압축 값은 종종 사용하는 유일한 유형의 값입니다. 예를 들어, 컴퓨터로 스트리밍되는 음악 / 비디오 패킷은 다음 값이 크기 최적화로 2 바이트인지 4 바이트인지를 지정하는 데 약간의 시간을 소비 할 수 있습니다.

컴퓨터에 메모리를 사용할 수 있으면 메모리는 저렴하지만 크기 조정이 가능한 변수의 속도와 복잡성은 .. 그것이 유일한 이유입니다.


4
누군가 BigInteger를 언급하게되어 기쁩니다. 그것은 어리석은 아이디어가 아니라 매우 큰 숫자에 대해서만하는 것이 합리적이라는 것입니다.
Max Barraclough

1
당신이 실제로 매우 정확한 숫자를 의미 :) 음을 최소한의 BigDecimal의 경우 ...에 학자 연하려면
빌 K

2
그리고 이것은 c ++ 태그가 붙어 있기 때문에 아마도 Java의 Big *과 같은 아이디어 인 GMP C ++ 클래스 인터페이스를 언급 할 가치가 있습니다.
디지털 외상

20

동적 크기를 가진 간단한 유형을 갖는 것은 매우 복잡하고 계산이 무거 우므로. 이것이 가능 할지도 모르겠습니다.
컴퓨터는 값을 변경할 때마다 숫자가 몇 비트인지 확인해야합니다. 추가 작업이 상당히 많을 것입니다. 그리고 컴파일하는 동안 변수의 크기를 모르면 계산을 수행하기가 훨씬 어려울 것입니다.

동적 크기의 변수를 지원하려면 컴퓨터는 실제로 변수에 몇 바이트가 있는지 기억해야합니다.이 정보를 저장하려면 추가 메모리가 필요합니다. 그리고이 정보는 변수에 대한 모든 작업 전에 올바른 프로세서 명령을 선택하기 전에 분석해야합니다.

컴퓨터의 작동 방식과 변수의 크기가 일정한 이유를 더 잘 이해하려면 어셈블러 언어의 기초를 배우십시오.

그러나 constexpr 값을 사용하여 이와 같은 것을 달성 할 수 있다고 생각합니다. 그러나 이것은 프로그래머가 코드를 예측하기 어렵게 만듭니다. 일부 컴파일러 최적화는 이와 같은 작업을 수행 할 수 있지만 프로그래머에게 숨기고 간단하게 유지합니다.

여기에서는 프로그램 성능과 관련된 문제에 대해서만 설명했습니다. 변수의 크기를 줄임으로써 메모리를 절약하기 위해 해결해야 할 모든 문제를 생략했습니다. 솔직히, 나는 그것이 가능하다고 생각하지 않습니다.


결론적으로 선언 된 것보다 작은 변수를 사용하는 것은 컴파일 중에 값이 알려진 경우에만 의미가 있습니다. 현대 컴파일러가 그렇게 할 가능성이 높습니다. 다른 경우에는 너무 많거나 해결 불가능한 문제가 발생할 수 있습니다.


나는 컴파일 타임에 그러한 일이 이루어 졌음을 의심합니다. 컴파일러 메모리를 보존하는 데는 별다른 의미가 없으며, 이것이 유일한 이점입니다.
Bartek Banachewicz 2018 년

1
constexpr 변수에 일반 변수를 곱하는 것과 같은 작업에 대해 생각하고있었습니다. 예를 들어 값이있는 8 바이트 constexpr 변수 56가 있고 2 바이트 변수를 곱합니다. 일부 아키텍처에서는 64 비트 연산에 더 많은 계산이 필요하므로 컴파일러는 16 비트 곱셈 만 수행하도록 최적화 할 수 있습니다.
NO_NAME

SNOBOL 제품군의 일부 APL 구현 및 일부 언어 (SPITBOL은 생각하십니까? 어쩌면 아이콘)는 정확하게이 작업을 세분화했습니다. 실제 값에 따라 표현 형식을 동적으로 변경합니다. APL은 부울에서 정수로 이동하여 부동 및 역으로 이동합니다. SPITBOL은 부울 (바이트 배열에 저장된 8 개의 개별 부울 배열)의 열 표시에서 정수 (IIRC)로 이동합니다.
davidbak

16

그런 다음 myInt컴파일러에서 4 바이트를 차지합니다. 그러나 실제 값 255은 1 바이트로만 표현할 수 있습니다. 왜 myInt1 바이트의 메모리 만 차지하지 않습니까?

이를 가변 길이 인코딩 이라고하며 VLQ 와 같은 다양한 인코딩이 정의 되어 있습니다. 그러나 가장 유명한 것 중 하나는 아마도 UTF-8 일 것입니다 . UTF-8은 가변 바이트 수의 코드 포인트를 1에서 4까지 인코딩합니다.

또는보다 일반적인 방법은 다음과 같습니다. 값을 나타내는 데 필요한 공간이 해당 크기보다 작을 때 왜 유형에 하나의 크기 만 연결되어 있습니까?

엔지니어링에서 항상 그렇듯이 트레이드 오프에 관한 것입니다. 장점 만있는 솔루션은 없으므로 솔루션을 설계 할 때 장점과 절충점의 균형을 유지해야합니다.

정착 된 디자인은 고정 된 크기의 기본 유형을 사용하는 것이었고 하드웨어 / 언어는 거기서 내려 왔습니다.

그렇다면 가변 인코딩근본적인 약점은 무엇입니까? 임의의 주소 지정 없음 .

UTF-8 문자열에서 4 번째 코드 포인트가 시작되는 바이트의 인덱스는 무엇입니까?

이전 코드 포인트의 값에 따라 선형 스캔이 필요합니다.

무작위 주소 지정에서 더 나은 가변 길이 인코딩 체계가 있습니까?

예, 그러나 더 복잡합니다. 이상적인 것이 있다면 아직 본 적이 없습니다.

임의 주소 지정이 실제로 중요합니까?

어 그래!

문제는 모든 종류의 집계 / 배열이 고정 크기 유형에 의존한다는 것입니다.

  • struct? 의 3 번째 필드에 액세스 랜덤 어드레싱!
  • 배열의 세 번째 요소에 액세스하고 있습니까? 랜덤 어드레싱!

이는 본질적으로 다음과 같은 상충 관계가 있음을 의미합니다.

고정 크기 유형 또는 선형 메모리 스캔


이것은 소리를내는 것만 큼 큰 문제는 아닙니다. 항상 벡터 테이블을 사용할 수 있습니다. 메모리 오버 헤드와 추가 페치가 있지만 선형 스캔은 필요하지 않습니다.
Artelius

2
@Artelius : 정수의 너비가 가변 일 때 벡터 테이블을 어떻게 인코딩합니까? 또한 메모리에서 1-4 바이트를 사용하는 정수에 대해 1을 인코딩 할 때 벡터 테이블의 메모리 오버 헤드는 무엇입니까?
Matthieu M.

OP가 제공 한 특정 예에서 벡터 테이블을 사용하면 유리한 점이 없습니다. 벡터 테이블을 작성하는 대신 고정 크기 요소 배열에 데이터를 넣을 수도 있습니다. 그러나 OP는 더 일반적인 답변을 요청했습니다. 파이썬에서 정수 배열 이며 , 가변 크기의 정수 벡터 테이블! 그것은 문제를 해결하기 때문 아니라 파이썬이 컴파일 타임에 목록 요소가 모두 크기가 다른 정수, 부동 소수점, 딕트, 문자열 또는 목록인지 알지 못하기 때문입니다.
Artelius

@Artelius : 파이썬에서 배열은 요소에 대한 고정 크기 포인터 를 포함합니다. 이것은 간접적 인 대가로 요소에 도달하는 것을 O (1)로 만듭니다.
Matthieu M.

16

컴퓨터 메모리는 특정 크기 (종종 8 비트, 바이트라고 함)의 연속적으로 주소가 지정된 청크로 세분화되며 대부분의 컴퓨터는 연속적인 주소를 갖는 바이트 시퀀스에 효율적으로 액세스하도록 설계되었습니다.

객체의 주소가 객체의 수명 내에서 변경되지 않으면 해당 주소가 지정된 코드가 해당 객체에 빠르게 액세스 할 수 있습니다. 그러나이 접근 방식의 필수 제한 사항은 주소 X에 주소를 할당 한 다음 N 바이트 떨어진 주소 Y에 다른 주소를 할당하면 X가 수명 내에서 N 바이트보다 커질 수 없다는 것입니다. X 또는 Y가 이동하지 않는 한 Y X가 이동하려면 X의 주소를 보유한 유니버스의 모든 내용이 새 주소를 반영하도록 업데이트되고 Y도 이동해야합니다. 그러한 업데이트를 용이하게하는 시스템을 설계 할 수는 있지만 (Java 및 .NET 모두 꽤 잘 관리함) 일생 동안 동일한 위치에 머무를 객체를 다루는 것이 훨씬 효율적입니다.


"X 또는 Y가 이동하지 않으면 X는 Y의 수명 내에서 N 바이트보다 커질 수 없습니다. X가 이동하려면 X의 주소를 보유하는 유니버스의 모든 내용을 반영하여 업데이트해야합니다. 새로운 것과 마찬가지로 Y도 움직입니다. " 현재 값을 요구 추가해야합니다만큼 크기 만 사용 객체 :이 IMO 두드러진 점은 하나 숙고 어떻게 할 수 이제까지 작업 크기 / 센티넬, 메모리 이동, 참조 그래프, 등 그리고 매우 분명한에 대한 오버 헤드를 ...하지만 여전히, 특히 다른 사람들이 적은 것처럼 명확하게 진술 할 가치가 있습니다.
underscore_d

@underscore_d : 가변 크기의 객체를 처리하기 위해 처음부터 설계된 Javascript와 같은 언어는 놀랍도록 효율적일 수 있습니다. 반면에 가변 크기 객체 시스템을 간단하게 만들 수는 있지만 빠른 속도를 구현할 수는 있지만 간단한 구현은 느리고 빠른 구현은 매우 복잡합니다.
supercat

13

짧은 대답은 다음과 같습니다. C ++ 표준이 그렇게 말했기 때문입니다.

긴 대답은 : 컴퓨터에서 할 수있는 일은 궁극적으로 하드웨어에 의해 제한됩니다. 물론 저장을 위해 정수를 가변 바이트 수로 인코딩하는 것이 가능하지만이를 읽으려면 특별한 CPU 명령이 필요하거나 소프트웨어로 구현할 수 있지만 너무 느릴 것입니다. 미리 정의 된 너비의 값을로드하기 위해 CPU에서 고정 크기 작업을 사용할 수 있으며 가변 너비에 대한 값은 없습니다.

고려해야 할 또 다른 사항은 컴퓨터 메모리의 작동 방식입니다. 정수 유형이 1-4 바이트의 저장 영역을 차지할 수 있다고 가정하십시오. 값 42를 정수에 저장한다고 가정합니다. 1 바이트를 차지하고 메모리 주소 X에 배치합니다. 그런 다음 X + 1 위치에 다음 변수를 저장합니다 (이 시점에서는 정렬을 고려하지 않습니다). . 나중에 값을 6424로 변경하기로 결정했습니다.

그러나 이것은 단일 바이트에 맞지 않습니다! 그래서 당신은 무엇을합니까? 나머지는 어디에 두나요? X + 1에 이미 무언가가 있으므로 배치 할 수 없습니다. 다른 곳? 나중에 어디에서 어떻게 알 수 있습니까? 컴퓨터 메모리는 삽입 의미론을 지원하지 않습니다. 위치에 무언가를 놓고 공간을 확보하기 위해 모든 것을 옆으로 밀어 넣을 수는 없습니다!

따로 : 당신이 말하는 것은 실제로 데이터 압축 영역입니다. 압축 알고리즘은 모든 것을 더 단단히 압축하기 위해 존재하므로 적어도 일부는 필요한 것보다 정수에 더 많은 공간을 사용하지 않는 것이 좋습니다. 그러나 압축 된 데이터는 수정하기 쉽지 않으며 (가능한 경우) 변경할 때마다 다시 압축됩니다.


11

이렇게하면 런타임 성능이 상당히 향상됩니다. 가변 크기 유형으로 작업하려면 작업을 수행하기 전에 각 숫자를 디코딩해야하고 (기계 코드 명령어는 일반적으로 고정 너비) 작업을 수행 한 다음 결과를 보유 할만큼 충분한 공간을 메모리에서 찾으십시오. 그것들은 매우 어려운 작업입니다. 모든 데이터를 약간 비효율적으로 저장하는 것이 훨씬 쉽습니다.

이것이 항상 수행되는 방법은 아닙니다. Google의 Protobuf 프로토콜을 고려하십시오. 프로토 타입은 데이터를 매우 효율적으로 전송하도록 설계되었습니다. 전송 된 바이트 수를 줄이면 데이터를 조작 할 때 추가 명령 비용이들 수 있습니다. 따라서 프로토 타입은 1, 2, 3, 4 또는 5 바이트의 정수를 인코딩하는 인코딩을 사용하며 작은 정수는 더 적은 바이트를 사용합니다. 그러나 메시지가 수신되면보다 전통적인 고정 크기 정수 형식으로 압축 해제되어 작동하기가 더 쉽습니다. 네트워크 전송 중에 만 공간 효율적인 가변 길이 정수를 사용합니다.


11

나는 Sergey의 집 비유를 좋아 하지만 자동차 비유가 더 좋을 것이라고 생각합니다.

변수 유형을 자동차 유형으로, 사람을 데이터로 상상해보십시오. 우리는 새 차를 찾을 때 목적에 가장 적합한 차를 선택합니다. 한 두 사람 만 들어갈 수있는 작은 스마트 자동차를 원하십니까? 아니면 더 많은 사람들을 태울 리무진? 둘 다 속도와 주행 거리 (속도와 메모리 사용량을 고려)와 같은 장점과 단점이 있습니다.

리무진이 있고 혼자 운전하는 경우 자신에게만 맞게 줄지 않습니다. 그러기 위해서는 차를 팔고 (읽기 : 할당 해제) 새로운 작은 차를 사야합니다.

비유를 계속하면서, 당신은 기억을 자동차로 가득 찬 거대한 주차장이라고 생각할 수 있습니다. 그리고 당신이 읽을 때, 당신의 차 유형만을 위해 훈련 된 전문 운전사가 그것을 가져옵니다. 차 안에있는 사람들에 따라 차가 종류를 바꿀 수 있다면, 어떤 종류의 차가 그 자리에 앉을 지 알 수 없으므로 차를 타기를 원할 때마다 모든 운전 기사를 가져와야합니다.

다시 말해, 런타임시 읽어야 할 메모리 양을 결정하는 것은 매우 비효율적이며 주차장에 몇 대의 자동차를 더 장착 할 수 있다는 사실보다 중요합니다.


10

몇 가지 이유가 있습니다. 하나는 임의의 크기의 숫자를 처리하기위한 복잡성이 추가되고 컴파일러가 더 이상 모든 int의 길이가 정확히 X 바이트라는 가정을 기반으로 최적화 할 수 없기 때문에 성능이 저하되는 것입니다.

두 번째 방법은 간단한 형식을 이런 식으로 저장하면 길이를 유지하기 위해 추가 바이트가 필요하다는 것입니다. 따라서이 새로운 시스템에서는 255 이하의 값이 실제로는 2 바이트가 필요하지만 최악의 경우 이제 4 대신 5 바이트가 필요합니다. 이는 사용 된 메모리 측면에서 성능 승리가 예상보다 적음을 의미합니다. 생각하고 어떤 경우에는 실제로 순 손실이 될 수 있습니다.

세 번째 이유는 컴퓨터 메모리가 일반적으로 바이트가 아닌 단어 로 처리 될 수 있기 때문입니다 . (그러나 각주 참조). 워드는 바이트의 배수로 보통 32 비트 시스템에서 4, 64 비트 시스템에서 8입니다. 일반적으로 개별 바이트를 읽을 수 없으며 단어를 읽고 해당 단어에서 n 번째 바이트를 추출합니다. 즉, 단어에서 개별 바이트를 추출하는 것은 전체 단어를 읽는 것보다 조금 더 많은 노력이 필요하며 전체 메모리가 단어 크기 (즉, 4 바이트 크기) 청크로 나누어지면 매우 효율적입니다. 임의의 크기의 정수가 떠 다니면 정수의 한 부분이 한 단어에 있고 다른 단어가 다음 단어에있을 수 있으므로 전체 정수를 얻으려면 두 번의 읽기가 필요합니다.

각주 :보다 정확하게 바이트 단위로 주소를 지정하면 대부분의 시스템은 '고르지 않은'바이트를 무시했습니다. 즉, 주소 0, 1, 2 및 3은 모두 같은 단어를 읽고 4, 5, 6 및 7은 다음 단어를 읽습니다.

발표되지 않은 메모에서 32 비트 시스템의 메모리가 최대 4GB 인 이유도 여기에 있습니다. 메모리의 위치를 ​​지정하는 데 사용되는 레지스터는 일반적으로 단어를 보유 할 수있을만큼 충분히 큽니다. 즉, 최대 값은 (2 ^ 32) -1 = 4294967295입니다. 4294967296 바이트는 4GB입니다.


8

어떤 의미에서 C ++ 표준 라이브러리에는 가변 크기를 갖는 객체가 있습니다 (예 :) std::vector. 그러나 이들은 모두 필요한 여분의 메모리를 동적으로 할당합니다. 를 취 sizeof(std::vector<int>)하면 객체가 관리하는 메모리와는 아무런 관련이없는 상수를 얻게되고을 포함하는 배열이나 구조를 할당 std::vector<int>하면 추가 스토리지를 동일한 배열이나 구조에 넣지 않고이 기본 크기를 예약합니다 . 가변 길이 배열 및 구조와 같은 것을 지원하는 몇 가지 C 구문이 있지만 C ++에서는이를 지원하지 않았습니다.

언어 표준은 컴파일러가 효율적인 코드를 생성 할 수 있도록 객체 크기를 정의합니다. 예를 들어, int일부 구현에서 길이가 4 바이트 인 경우 a포인터 또는 int값의 배열로 선언 한 다음 a[i]의사 코드 "주소를 + 4 × i 역 참조"로 변환합니다. 이것은 일정한 시간에 수행 될 수 있으며, x86과 C가 원래 개발 된 DEC PDP 시스템을 포함한 많은 명령 세트 아키텍처가 단일 시스템 명령으로 수행 할 수있는 일반적이고 중요한 작업입니다.

가변 길이 단위로 연속적으로 저장된 데이터의 일반적인 실제 예는 UTF-8로 인코딩 된 문자열입니다. (단, 컴파일러에 UTF-8 문자열의 기본 형식이 여전히 char1. 폭이 ASCII 문자열이 유효한 UTF-8 및 라이브러리 코드 등의 많은으로 해석 할 수 있도록하고있다 strlen()strncpy()작업을 계속.) UTF-8 코드 포인트의 인코딩 길이는 1-4 바이트 일 수 있으므로 문자열에서 다섯 번째 UTF-8 코드 포인트를 원하면 데이터의 5 번째 바이트에서 17 번째 바이트까지 어디서나 시작할 수 있습니다. 그것을 찾는 유일한 방법은 문자열의 시작 부분부터 스캔하여 각 코드 포인트의 크기를 확인하는 것입니다. 다섯 번째 그래프 를 찾으려면캐릭터 클래스도 확인해야합니다. 문자열에서 백만 번째 UTF-8 문자를 찾으려면이 루프를 백만 번 실행해야합니다! 인덱스를 자주 사용해야하는 경우 문자열을 한 번 통과하여 인덱스를 만들거나 UCS-4와 같은 고정 폭 인코딩으로 변환 할 수 있습니다. 문자열에서 백만 번째 UCS-4 문자를 찾는 것은 배열 주소에 4 백만을 추가하는 것입니다.

가변 길이 데이터의 또 다른 문제는 할당 할 때 사용할 수있는만큼의 메모리를 할당하거나 필요에 따라 동적으로 재 할당해야한다는 것입니다. 최악의 경우를 할당하면 매우 낭비가 될 수 있습니다. 연속적인 메모리 블록이 필요한 경우 재 할당하면 모든 데이터를 다른 위치로 복사해야하지만 비 연속적인 청크에 메모리를 저장하면 프로그램 논리가 복잡해집니다.

그래서, 가변 길이 bignums 대신 고정 폭을 가질 수있어 short int, int, long intlong long int, 그러나 할당하고 사용하는 것이 비효율적이다. 또한 모든 주류 CPU는 고정 폭 레지스터에서 산술을 수행하도록 설계되었으며 어떤 종류의 가변 길이 빅넘에서 직접 작동하는 명령도 없습니다. 그것들은 소프트웨어에서 훨씬 느리게 구현되어야합니다.

현실 세계에서, 대부분의 (전부는 아님) 프로그래머들은 UTF-8 인코딩의 이점, 특히 호환성이 중요하며, 문자열을 앞뒤로 스캔하거나 블록을 복사하는 것 외에는 거의 신경 쓰지 않기로 결정했습니다. 가변 폭의 단점이 허용되는 메모리. 우리는 다른 것들을 위해 UTF-8과 비슷한 압축 된 가변 너비 요소를 사용할 수 있습니다. 그러나 우리는 거의하지 않으며 표준 라이브러리에 없습니다.


7

값을 나타내는 데 필요한 공간이 해당 크기보다 작을 때 유형에 하나의 크기 만 연관된 이유는 무엇입니까?

주로 정렬 요구 사항으로 인해.

basic.align / 1 :

객체 유형에는 해당 유형의 객체가 할당 될 수있는 주소를 제한하는 정렬 요구 사항이 있습니다.

층이 많고 각 층에 방이 많은 건물을 생각해보십시오.
각 방은 N 명의 사람이나 물건을 담을 수있는 당신의 크기 (고정 공간)입니다.
미리 알려진 방 크기로 건물의 구조적 구성 요소가 잘 구조화 됩니다.

방이 정렬되지 않으면 건물 골격이 잘 구성되지 않습니다.


7

더 적을 수 있습니다. 기능을 고려하십시오.

int foo()
{
    int bar = 1;
    int baz = 42;
    return bar+baz;
}

어셈블리 코드로 컴파일합니다 (g ++, x64, 세부 정보 제거)

$43, %eax
ret

여기에, bar그리고 baz표현하기 위해 0 바이트를 사용하게.


5

그렇다면 왜 myInt는 단지 1 바이트의 메모리를 차지하지 않습니까?

그렇게 많이 사용하라고했기 때문입니다. 을 사용할 때 unsigned int일부 표준에 따르면 4 바이트가 사용되며 사용 가능한 범위는 0 ~ 4,294,967,295가됩니다. unsigned char대신 대신 사용하려는 경우 표준에 따라 1 바이트 만 사용하고 아마도 C ++은 일반적으로 이러한 표준을 사용합니다.

이러한 표준이 아니었다면 이것을 명심해야 할 것입니다. 컴파일러 나 CPU는 4 대신 1 바이트 만 사용하는 방법을 알고 있습니까? 나중에 프로그램에서 해당 값을 더하거나 곱하면 더 많은 공간이 필요합니다. 메모리를 할당 할 때마다 OS는 해당 공간을 찾아 매핑하고 제공해야합니다 (잠재적으로 메모리를 가상 RAM으로 교체). 시간이 오래 걸릴 수 있습니다. 미리 메모리를 할당하면 다른 할당이 완료 될 때까지 기다릴 필요가 없습니다.

바이트 당 8 비트를 사용하는 이유는 다음과 같습니다. 바이트가 8 비트 인 이유는 무엇입니까?

참고로 정수가 오버플로되도록 허용 할 수 있습니다. 그러나 부호있는 정수를 사용해야하는 경우 C \ C ++ 표준에 따르면 정수 오버플로로 인해 정의되지 않은 동작이 발생합니다. 정수 오버플로


5

대부분의 답변이 그리워 보이는 간단한 것 :

C ++의 디자인 목표에 적합하기 때문입니다.

컴파일 타임에 유형의 크기를 계산할 수 있으면 컴파일러와 프로그래머가 단순화 할 수있는 수많은 가정을 만들 수 있으며, 특히 성능과 관련하여 많은 이점이 있습니다. 물론 고정 크기 유형에는 정수 오버플로와 같은 함정이 있습니다. 이것이 언어마다 다른 디자인 결정을 내리는 이유입니다. 예를 들어 Python 정수는 기본적으로 가변 크기입니다.

아마도 C ++이 고정 크기 유형에 너무 많이 의존하는 주된 이유는 C 호환성의 목표 때문입니다. 그러나 C ++은 매우 효율적인 코드를 생성하려고 시도하는 정적 형식 언어이므로 프로그래머가 명시 적으로 지정하지 않은 것을 추가하지 않으므로 고정 크기 형식은 여전히 ​​의미가 있습니다.

그렇다면 왜 C가 처음에 고정 크기 유형을 선택 했습니까? 단순한. 70 년대 운영 체제, 서버 소프트웨어 및 유틸리티를 작성하도록 설계되었습니다. 다른 소프트웨어에 인프라 (예 : 메모리 관리)를 제공 한 것. 이렇게 낮은 수준에서는 성능이 중요하므로 컴파일러가 사용자가 지시 한대로 정확하게 수행합니다.


5

변수의 크기를 변경하려면 재 할당이 필요하며 이는 일반적으로 몇 바이트의 메모리를 낭비하는 것과 비교하여 추가 CPU주기의 가치가 없습니다.

지역 변수는 스택의 크기가 변하지 않을 때 조작하기에 매우 빠릅니다. 변수의 크기를 1 바이트에서 2 바이트로 확장하기로 결정한 경우 스택의 모든 공간을 1 바이트 씩 이동하여 해당 공간을 확보해야합니다. 이동해야하는 항목 수에 따라 CPU주기가 많이 소요될 수 있습니다.

또 다른 방법은 모든 변수를 힙 위치에 대한 포인터로 만드는 것이지만 실제로는 더 많은 CPU주기와 메모리를 낭비하게됩니다. 포인터는 4 바이트 (32 비트 주소 지정) 또는 8 바이트 (64 비트 주소 지정)이므로 포인터에 이미 4 또는 8을 사용한 다음 힙에있는 데이터의 실제 크기입니다. 이 경우 여전히 재 할당 비용이 있습니다. 힙 데이터를 재 할당해야하는 경우 운이 좋으며 인라인으로 확장 할 공간이있을 수 있지만 때로는 원하는 크기의 연속 메모리 블록을 갖기 위해 힙의 다른 곳으로 이동해야 할 수도 있습니다.

미리 사용할 메모리 양을 결정하는 것이 항상 더 빠릅니다. 동적 크기 조정을 피할 수 있으면 성능이 향상됩니다. 메모리 낭비는 일반적으로 성능 향상의 가치가 있습니다. 그래서 컴퓨터에는 많은 양의 메모리가 있습니다. :)


3

컴파일러는 여전히 작동하는 한 ( "있는 그대로"규칙) 코드를 많이 변경할 수 있습니다.

full을 이동하는 데 필요한 더 긴 (32/64 비트) 대신 8 비트 리터럴 이동 명령어를 사용할 수 있습니다 int. 그러나로드를 수행하기 전에 먼저 레지스터를 0으로 설정해야하므로로드를 완료하려면 두 개의 명령이 필요합니다.

32 비트로 값을 처리하는 것이 더 효율적입니다 (적어도 메인 컴파일러에 따르면). 실제로 인라인 어셈블리없이 8 비트로드를 수행하는 x86 / x86_64 컴파일러를 아직 보지 못했습니다.

그러나 64 비트에서는 상황이 다릅니다. 인텔은 프로세서의 이전 확장 (16 비트에서 32 비트까지)을 설계 할 때 실수를 범했습니다. 다음 은 그들이 어떻게 보이는지에 대한 좋은 표현입니다. 여기서 중요한 점은 AL 또는 AH에 쓸 때 다른 하나는 영향을받지 않습니다 (충분히, 그것은 요점이며 당시에는 의미가있었습니다). 그러나 32 비트로 확장하면 흥미로워집니다. 최하위 비트 (AL, AH 또는 AX)를 쓰면 EAX의 상위 16 비트에는 아무 변화가 없습니다. 즉,를 a char로 승격 int하려면 먼저 해당 메모리를 지워야하지만 방법은 없습니다. 실제로이 상위 16 비트 만 사용하여이 "기능"을 다른 무엇보다 고통스럽게 만듭니다.

이제 64 비트로 AMD는 훨씬 더 나은 작업을 수행했습니다. 하위 32 비트에서 아무 것도 터치하면 상위 32 비트는 단순히 0으로 설정됩니다. 이는이 godbolt 에서 볼 수있는 실제 최적화로 이어집니다 . 8 비트 또는 32 비트의로드가 동일한 방식으로 수행되는 것을 볼 수 있지만 64 비트 변수를 사용할 경우 컴파일러는 리터럴의 실제 크기에 따라 다른 명령어를 사용합니다.

따라서 여기에서 볼 수 있듯이 컴파일러는 동일한 결과를 생성하는 경우 CPU 내부에서 변수의 실제 크기를 완전히 변경할 수 있지만 더 작은 유형의 경우에는 의미가 없습니다.


수정 : as-if . 또한 더 짧은로드 / 스토어를 사용할 수 있다면 다른 바이트를 사용 가능하게 만드는 방법을 알지 못합니다. 현재 값에 필요하지 않은 메모리 만지지 않는 것이 아니라 OP가 궁금해하는 것처럼 보입니다. 그러나 읽을 바이트 수를 말하고 런타임에 모든 RAM을 마술처럼 바꿀 수 있으므로 공간 효율성에 대한 이상한 철학적 아이디어 (거대한 성능 비용은 신경 쓰지 마십시오!)가 충족됩니다. '해결'하지 않습니다. CPU / OS가해야 할 일이 너무 복잡하여 가장 명확하게 질문에 답할 수 있습니다.
underscore_d

1
그래도 레지스터에 "메모리를 저장할 수"없습니다. AH 및 AL을 악용하여 이상한 작업을 시도하지 않는 한 동일한 범용 레지스터에서 여러 가지 다른 값을 가질 수는 없습니다. 지역 변수는 종종 레지스터에 남아 있고 필요하지 않은 경우에는 RAM으로 가지 않습니다.
meneldal 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.