bestsource

가깝고 멀고 거대한 포인터는 무엇입니까?

bestsource 2023. 9. 16. 09:32
반응형

가깝고 멀고 거대한 포인터는 무엇입니까?

누가 이 포인터들을 적절한 예를 들어 설명해 줄 수 있습니까? 그리고 이 포인터들이 언제 사용되는지요?

인텔 X86 아키텍처가 대표적인 예입니다.

인텔 8086은 내부적으로 16비트 프로세서였습니다. 모든 레지스터의 너비는 16비트였습니다.그러나 주소 버스의 폭은 20비트(1 MiB)였습니다.이것은 당신이 처음 64 kiB로 제한하면서, 전체 주소를 레지스터에 담을 수 없다는 것을 의미했습니다.

인텔의 해결책은 16비트의 "세그먼트 레지스터"를 만드는 것이었습니다. 16비트의 "세그먼트 레지스터"는 내용이 4비트로 변경되어 주소에 추가됩니다.예를 들어,

DS ("Data Segment") register:  1234 h
DX ("D eXtended") register:   + 5678h
                              ------
Actual address read:           179B8h

이것은 64 kiB 세그먼트의 개념을 만들었습니다.따라서 "근접" 포인터는 DX 레지스터의 내용(5678h)일 뿐이며 DS 레지스터가 이미 올바르게 설정되어 있지 않으면 무효가 됩니다. 반면 "근접" 포인터는 32비트(12345678h, DS 뒤에 DX가 있음)이므로 항상 작동합니다(그러나 레지스터 2개를 로드한 후 완료되면 DS 레지스터를 복원해야 하므로 속도가 느렸습니다).

(아래의 슈퍼캣 노트와 같이 오버플로된 DX에 오프셋이 "롤오버" 후 DS에 추가되어 최종 주소를 얻습니다.이를 통해 16비트 오프셋은 DX가 가리키는 부분에서 ± 32kiB인 부분뿐만 아니라 64kiB 세그먼트의 모든 주소에 액세스할 수 있었습니다. 일부 명령에서 16비트 상대 오프셋 주소 지정을 사용하는 다른 아키텍처에서와 마찬가지로 말입니다.)

그러나 다른 값이지만 동일한 주소를 가리키는 두 개의 "먼" 포인터를 가질 수 있습니다.예를 들어, 파 포인터 100079B8h는 12345678h와 같은 위치를 가리킵니다.따라서 먼 포인터의 포인터 비교는 잘못된 연산이었습니다. 포인터가 다를 수 있지만 여전히 같은 위치를 가리킵니다.

여기서 저는 (당시 모토로라 68000 프로세서를 탑재한) Mac이 그다지 나쁘지 않다고 판단했기 때문에 큰 지침을 놓쳤습니다.IIRC는 두 번째 예에서와 같이 세그먼트 레지스터의 모든 중첩 비트가 0임을 보장하는 먼 포인터에 불과했습니다.

모토로라는 6800 시리즈의 프로세서가 64kiB로 제한되었기 때문에 이러한 문제가 없었습니다. 68000 아키텍처를 만들었을 때 32비트 레지스터로 바로 이동했기 때문에 가깝거나 멀리 있거나 큰 포인터가 필요하지 않았습니다. (대신에, 그들의 문제는 주소의 하위 24비트만이 실제로 중요해서 어떤 프로그램이 필요하다는 것이었습니다.ammers (유명한 애플)는 높은 8비트를 "포인트 플래그"로 사용하여 주소 버스가 32비트(4Gb)로 확장될 때 문제를 일으킵니다.

리누스 토르발스는 주소가 32비트인 "보호 모드"를 제공하는 80386까지 버텼고, 세그먼트 레지스터는 주소의 절반이 높아 추가할 필요가 없었고, 처음부터 리눅스를 작성해 보호 모드만 사용하고, 이상한 세그먼트 같은 것은 사용하지 않았습니다.그렇기 때문에 Linux에서 근거리 및 원거리 포인터 지원을 받지 못합니다. 새로운 아키텍처를 설계하는 회사가 Linux 지원을 원할 경우 다시 사용할 수 없는 이유입니다.그리고 그들은 로빈의 음유시인들을 먹었으며, 매우 기뻐했습니다.(예...)

먼 포인터와 큰 포인터의 차이:

는 으로 는 는 으로 near예를 들어 다음과 같습니다.int *p는 ㅇnear포인팅합니다.near16비트 컴파일러의 경우 포인터는 2바이트입니다.그리고 우리는 컴파일러마다 크기가 다르다는 것을 이미 잘 알고 있습니다. 그것들은 참조하는 포인터의 주소의 오프셋만 저장합니다.오프셋으로만 구성된 주소의 범위는 0 - 64K바이트입니다.

Far그리고.huge포인터:

Far그리고.huge포인터는 4바이트의 크기를 갖습니다.포인터가 참조하는 주소의 세그먼트와 오프셋을 모두 저장합니다.그렇다면 그들의 차이점은 무엇일까요?

파 포인터의 제한:

우리는 주어진 먼 주소의 세그먼트 주소에 산술 연산을 적용하여 변경하거나 수정할 수 없습니다.산술 연산자를 사용하면 한 세그먼트에서 다른 세그먼트로 점프할 수 없습니다.

세그먼트 주소를 증가시키는 대신 해당 오프셋 주소의 최대값을 초과하여 먼 주소를 증가시키면 해당 오프셋 주소가 순환 순서로 반복됩니다.를 래핑(rapping)이라고도 합니다이 π π(래핑)인 이라고도 합니다. 즉, 오프셋이 다음과 같은 경우0xffff그리고 우리는 1을 더하면 그것은0x0000로 만약 고로가면면가ed고f을 줄이면0x00001까지, 그 다음과 같습니다.0xffff세그먼트에 변화가 없다는 것을 기억해야 합니다.

이제 거대한 포인터와 먼 포인터를 비교해 보겠습니다.

1. 먼 포인터가 증가 또는 감소하는 경우 포인터의 오프셋만 실제로는 증가 또는 감소하지만 큰 포인터의 경우 세그먼트 및 오프셋 값이 모두 변경됩니다.

여기서 가져온 다음 예를 생각해 보십시오.

 int main()
    {
    char far* f=(char far*)0x0000ffff;
    printf("%Fp",f+0x1);
    return 0;
  }

출력은 다음과 같습니다.

0000:0000

세그먼트 값에는 변화가 없습니다.

거대한 포인터의 경우:

int main()
{
char huge* h=(char huge*)0x0000000f;
printf("%Fp",h+0x1);
return 0;
}

출력은 다음과 같습니다.

0001:0000

이는 증분 연산이 오프셋 값뿐만 아니라 세그먼트 값도 변하기 때문입니다., 는 의 합니다 되지 을 가 합니다 을 는 의 되지 가 far포인터를 사용하는 경우huge포인터는 한 세그먼트에서 다른 세그먼트로 이동할 수 있습니다.

2.를 먼 할 경우 됩니다. 관계 연산자가 먼 포인터에 사용될 경우 오프셋만 비교됩니다.즉, 관계 연산자는 비교되는 포인터의 세그먼트 값이 동일한 경우에만 먼 포인터에서 작동합니다.그리고 이런 일이 일어나지 않을 경우에는 절대 주소의 비교가 실제로 이루어집니다.를 해 해 를 far키워드:

int main()
{
char far * p=(char far*)0x12340001;
char far* p1=(char far*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

출력:

different

huge키워드:

int main()
{
char huge * p=(char huge*)0x12340001;
char huge* p1=(char huge*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

출력:

same

설명 : 우리 절대적주소 주소절대적 both for address the absolute we see 설명 우리: as for p그리고.p1이다 ㅇ12341(1234*10+1아니면1230*10+41)의하므로 첫 한 것으로 되지 않습니다 그러나 다음과 같은 경우가 있으므로 1차적인 경우에는 동등하다고 볼 수 없습니다.far만을다즉다을다r을다즉s,keylsidte0001==0041 그건 거짓입니다.

그리고 거대한 포인터의 경우, 동일한 절대 주소에 대해 비교 연산이 수행됩니다.

  1. 는 되지 a입니다.huge포인터는 정규화됩니다. 정규화된 포인터는 세그먼트에 가능한 한 많은 주소를 가진 포인터이며 오프셋이 15보다 크지 않음을 의미합니다.

    있다고 가정해 보겠습니다0x1234:1234면의된는는된의n면fes0x1357:0004(는 ( 는 는 )입니다13574). 되며, 에는 정규화되지 않습니다 거대한 포인터는 어떤 산술 연산을 수행해야만 정규화되며, 할당 중에는 정규화되지 않습니다.

     int main()
     {
      char huge* h=(char huge*)0x12341234;
      char huge* h1=(char huge*)0x12341234;
      printf("h=%Fp\nh1=%Fp",h,h1+0x1);
      return 0;
     }
    

    출력:

    h=1234:1234
    
    h1=1357:0005
    

    설명:huge할당된 경우 포인터가 정규화되지 않습니다.그러나 그것에 대한 산술 연산이 이루어지면 정상화되므로,h이다 ㅇ1234:1234그리고.h1이다 ㅇ1357:0005정상화된 것입니다.

    4.거대한 포인터의 오프셋은 정규화 때문에 16보다 작으며 먼 포인터의 경우에는 그렇지 않습니다.

    예를 들어 제가 말하고 싶은 것을 이해해 보겠습니다.

     int main()
      {
      char far* f=(char far*)0x0000000f;
      printf("%Fp",f+0x1);
      return 0;
      }
    

출력:

    0000:0010

huge키워드:

      int main()
      {
      char huge* h=(char huge*)0x0000000f;
        printf("%Fp",h+0x1);
        return 0;
        }

        Output:
        0001:0000

원시키면 :1 를 시키면 과 과 시키면 를 이 됩니다.0000:0010.가 거대한 될 그리고 우리가 거대한 포인터를 1씩 증가시킬때 그것은0001:0000오프셋이 15보다 클 수 없기 때문에 즉, 정규화됩니다.

예전에는 Turbo C 매뉴얼에 따르면 전체 코드와 데이터가 하나의 세그먼트에 들어갈 때 근접 포인터가 16비트에 불과했습니다.파 포인터는 세그먼트와 오프셋으로 구성되었지만 정규화는 수행되지 않았습니다.그리고 거대한 포인터가 자동으로 정상화되었습니다.두 개의 먼 포인터는 메모리 내의 동일한 위치를 가리킬 수 있지만 다른 반면 동일한 메모리 위치를 가리키는 정규화된 거대한 포인터는 항상 동일합니다.

이 답변의 모든 내용은 기존 8086 및 80286 세그먼트 메모리 모델과만 관련이 있습니다.

near: 64k 세그먼트의 모든 바이트를 처리할 수 있는 16비트 포인터

far: 세그먼트와 오프셋을 포함하는 32비트 포인터.세그먼트가 겹칠 수 있으므로 두 개의 다른 먼 포인터가 동일한 주소를 가리킬 수 있습니다.

higuous: 세그먼트가 "정규화"되어 같은 값을 가지지 않는 한 두 개의 먼 포인터가 같은 주소를 가리키지 않도록 하는 32비트 포인터.

티: 잼과 빵을 곁들인 음료.

그것은 우리를 다시 할 수 있게 할 것입니다 오 오 오 오 오

그리고 이 포인터들이 언제 사용됩니까?

1980년대와 90년대에 32비트 윈도우가 유비쿼터스화되기 전까지

일부 아키텍처에서는 시스템의 모든 개체를 가리킬 수 있는 포인터가 유용한 개체의 하위 집합을 가리킬 수 있는 포인터보다 크고 작업 속도가 느립니다.많은 사람들이 16비트 x86 아키텍처와 관련된 답변을 내놨습니다.16비트 시스템에서는 다양한 유형의 포인터가 일반적이었지만 구현 방식에 따라 64비트 시스템에서는 거의/두려움의 차이가 다시 나타날 수 있습니다(많은 경우 낭비가 심하겠지만 많은 개발 시스템이 모든 것에 대해 64비트 포인터로 이동한다고 해도 놀라지 않을 것입니다).

많은 프로그램에서 메모리 사용량을 두 가지 범주로 세분화하는 것은 매우 쉽습니다. 즉, 전체 용량이 64K 또는 4GB에 이르지만 자주 액세스할 수 있는 작은 것과 훨씬 더 많은 용량에 액세스할 수 있지만 자주 액세스할 필요가 없는 큰 것입니다.응용프로그램이 "큰 것" 영역에서 개체의 일부를 사용해야 할 경우 해당 부분을 "작은 것" 영역에 복사하여 작업하고 필요한 경우 다시 씁니다.

일부 프로그래머들은 "근접" 메모리와 "먼" 메모리를 구분해야 한다고 불평하지만, 많은 경우 이러한 구분을 통해 컴파일러가 훨씬 더 나은 코드를 생성할 수 있습니다.

(참고: 많은 32비트 시스템에서도 메모리의 특정 영역은 별도의 지시 없이 직접 액세스할 수 있지만 다른 영역은 액세스할 수 없습니다.예를 들어, 68000 또는 ARM에서 글로벌 변수 저장소를 가리키는 레지스터를 유지하는 경우 해당 레지스터의 처음 32K(68000) 또는 2K(ARM) 내의 변수를 직접 로드할 수 있습니다.다른 곳에 저장된 변수를 가져오려면 주소를 계산하기 위한 추가 지침이 필요합니다.더 자주 사용되는 변수를 선호 영역에 배치하고 컴파일러에게 알려주면 더 효율적인 코드 생성이 가능합니다.

이 용어는 16비트 아키텍처에서 사용되었습니다.

16비트 시스템에서 데이터는 64Kb 세그먼트로 분할되었습니다.로드 가능한 각 모듈(프로그램 파일, 동적으로 로드된 라이브러리 등)에는 최대 64Kb의 데이터만 저장할 수 있는 관련 데이터 세그먼트가 있었습니다.

NEAR 포인터는 16비트 저장 장치가 있는 포인터로, 현재 모듈 데이터 세그먼트에서 데이터(전용)를 참조했습니다.

요구 사항으로 64Kb 이상의 데이터를 가진 16비트 프로그램은 FAR 포인터를 반환하는 특별한 할당기에 액세스할 수 있었습니다. FAR 포인터는 상위 16비트의 데이터 세그먼트 ID이고 하위 16비트의 데이터 세그먼트에 포인터가 있습니다.

그러나 규모가 큰 프로그램은 64Kb 이상의 연속 데이터를 처리하기를 원할 것입니다.HUGHUGE 포인터는 멀리 있는 포인터와 똑같이 생겼으며 32비트 저장공간을 가지고 있습니다. 그러나 할당자는 연속 ID를 가진 다양한 데이터 세그먼트를 배열하여 단순히 데이터 세그먼트 셀렉터를 증가시킴으로써 다음 64Kb 데이터 청크에 도달할 수 있도록 주의를 기울였습니다.

기본적인 C와 C++ 언어 표준들은 그들의 메모리 모델에서 이러한 개념들을 결코 공식적으로 인식하지 못하였습니다 - C 또는 C++ 프로그램의 모든 포인터들은 같은 크기여야 합니다.따라서 NEAR, FAR, HUGE 속성은 다양한 컴파일러 벤더가 제공하는 확장입니다.

언급URL : https://stackoverflow.com/questions/3575592/what-are-near-far-and-huge-pointers

반응형