문제

나는 알고 싶다 가정을 위반하는 아키텍처 아래에 나열했습니다.또한 모든 아키텍처에 대해 잘못된 가정이 있는지(즉, 완전히 잘못된 가정이 있는지) 알고 싶습니다.

  1. sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)

  2. 특정 아키텍처에 대한 모든 포인터의 메모리 내 표현은 가리키는 데이터 유형에 관계없이 동일합니다.

  3. 포인터의 메모리 내 표현은 아키텍처와 동일한 비트 길이의 정수와 동일합니다.

  4. 포인터 데이터 유형의 곱셈과 나눗셈은 컴파일러에서만 금지됩니다.메모:예, 이것이 말도 안되는 일이라는 것을 알고 있습니다.내 말은 - 이러한 잘못된 사용을 금지하는 하드웨어 지원이 있습니까?

  5. 모든 포인터 값은 단일 정수로 캐스팅될 수 있습니다.즉, 어떤 아키텍처가 여전히 세그먼트와 오프셋을 사용합니까?

  6. 포인터를 증가시키는 것은 추가하는 것과 같습니다. sizeof(the pointed data type) 포인터에 의해 저장된 메모리 주소에.만약에 p 이다 int32* 그 다음에 p+1 4바이트 뒤의 메모리 주소와 같습니다. p.

나는 인접한 가상 메모리 공간에서 사용되는 포인터에 가장 익숙합니다.그 사용법에 대해서는 일반적으로 수직선상의 주소로 생각하여 얻을 수 있습니다.스택 오버플로 질문을 참조하세요. 포인터 비교.

도움이 되었습니까?

해결책

나는 당신 에게이 모든 것에 대한 구체적인 예를 줄 수는 없지만 최선을 다하겠습니다.

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)

나는 내가 어디에 있는지 모른다 알다 이것은 거짓이지만 고려하십시오.

모바일 장치에는 종종 프로그램 코드 등이 저장되는 양의 읽기 전용 메모리가 있습니다. 읽기 전용 값 (const 변수)은 읽기 전용 메모리에 저장 될 수 있습니다. ROM 주소 공간은 일반 RAM 주소 공간보다 작기 때문에 포인터 크기도 다를 수 있습니다. 마찬가지로, 함수에 대한 포인터는 프로그램이로드 된이 읽기 전용 메모리를 가리킬 수 있고 그렇지 않으면 수정할 수없는이 읽기 전용 메모리를 가리킬 수 있으므로 크기가 다를 수 있습니다 (따라서 데이터를 저장할 수 없음).

그래서 나는 위의 내용이 유지되지 않는다는 것을 관찰 한 플랫폼을 모르지만 시스템이 그럴 수있는 곳을 상상할 수 있습니다.

주어진 아키텍처에 대한 모든 포인터의 메모리 표현은 지적 된 데이터 유형에 관계없이 동일합니다.

멤버 포인터 대 일반 포인터를 생각해보십시오. 그들은 같은 표현 (또는 크기)을 가지고 있지 않습니다. 멤버 포인터는 a로 구성됩니다 this 포인터 및 오프셋.

위와 같이, 일부 CPU는 상수 데이터를 별도의 메모리 영역에로드하여 별도의 포인터 형식을 사용하는 것이 가능합니다.

포인터의 메모리 표현은 아키텍처와 같은 비트 길이의 정수와 동일합니다.

비트 길이가 어떻게 정의되는지에 따라 다릅니다. :) an int 많은 64 비트 플랫폼에서 여전히 32 비트입니다. 그러나 포인터는 64 비트입니다. 이미 말했듯이, 세그먼트 메모리 모델이있는 CPU는 한 쌍의 숫자로 구성된 포인터가 있습니다. 마찬가지로, 멤버 포인터는 한 쌍의 숫자로 구성됩니다.

포인터 데이터 유형의 곱셈 및 분할은 컴파일러에 의해서만 금지됩니다.

궁극적으로 데이터 유형 만 포인터링합니다 존재하다 컴파일러에서. CPU와 함께 작동하는 것은 포인터가 아니라 정수 및 메모리 주소입니다. 따라서 포인터 유형에서 이러한 작업이 어디에도 없을 때가 없습니다. ~할 수 있었다 금지되어 있습니다. CPU가 C ++ 문자열 객체의 연결을 금지하도록 요청할 수도 있습니다. C ++ 문자열 유형은 생성 된 기계 코드가 아닌 C ++ 언어로만 존재하기 때문에 그렇게 할 수 없습니다.

그러나 당신이 무엇을 대답합니다 평균, Motorola 68000 CPU를 찾으십시오. 정수와 메모리 주소에 대한 별도의 레지스터가 있다고 생각합니다. 이는 그러한 무의미한 작업을 쉽게 금지 할 수 있음을 의미합니다.

모든 포인터 값은 단일 정수에 캐스트 될 수 있습니다.

당신은 거기에 안전합니다. C 및 C ++ 표준은 메모리 공간 레이아웃, CPU 아키텍처 및 기타 항목에 관계없이 항상 가능하다는 것을 보장합니다. 구체적으로, 그들은 구현 정의 매핑. 다시 말해, 포인터를 항상 정수로 변환 한 다음 해당 정수를 다시 변환하여 원래 포인터를 얻을 수 있습니다. 그러나 C/C ++ 언어는 중간 정수 값이 무엇인지에 대해 아무 말도하지 않습니다. 이는 개별 컴파일러와 대상 하드웨어에 달려 있습니다.

포인터를 증가시키는 것은 포인터에 의해 저장된 메모리 주소에 크기를 추가하는 것과 같습니다.

다시, 이것은 보장됩니다. 개념적으로 그것을 고려하면 포인터는 주소를 가리키지 않으면 물체, 그러면 이것은 완벽하게 이해됩니다. 포인터에 하나를 추가하면 분명히 다음 물체. 객체의 길이가 20 바이트 인 경우 포인터를 증가 시키면 20 바이트가 이동하여 다음으로 이동합니다. 물체.

포인터가 단지 선형 주소 공간의 메모리 주소 인 경우 기본적으로 정수 인 경우 1 점을 증가시켜 주소에 1을 추가합니다. 즉, 다음으로 이동합니다. 바이트.

마지막으로, 귀하의 질문에 대한 의견에서 언급했듯이 C ++는 언어 일뿐입니다. 어떤 아키텍처가 컴파일되는지 신경 쓰지 않습니다. 이러한 한계 중 다수는 현대 CPU에서 모호한 것처럼 보일 수 있습니다. 그러나 Yesteryear의 CPU를 목표로한다면 어떨까요? 앞으로 10 년간의 CPU를 목표로한다면 어떨까요? 당신은 그들이 어떻게 작동하는지조차 알지 못하므로 그들에 대해 많이 가정 할 수 없습니다. 가상 머신을 타겟팅하고 있다면 어떨까요? 웹 사이트에서 실행할 준비가 된 플래시의 바이트 코드를 생성하는 컴파일러가 이미 존재합니다. C ++를 Python 소스 코드로 컴파일하려면 어떻게해야합니까?

표준에 명시된 규칙 내에서 유지하면 코드가 작동 할 것입니다. 모두 이 경우.

다른 팁

특정 실제 예제가 없지만 "권한"은 C 표준입니다. 표준이 요구하지 않는 경우 의도적으로 다른 가정을 준수하지 않는 준수 구현을 구축 할 수 있습니다. 이러한 가정 중 일부는 프로세서에 의해 직접 가져올 수있는 메모리 주소를 나타내는 정수로 포인터를 구현하는 것이 편리하기 때문에 대부분의 경우 사실입니다. 그러나 이것은 "편의"의 결과 일 뿐이며 다음과 같이 유지할 수 없습니다. 보편적 인 진실.

  1. 표준에 의해 요구되지 않는다 (이 질문을 참조하십시오). 예를 들어, sizeof(int*) 동등 할 수 있습니다 size(double*). void* 포인터 값을 저장할 수 있어야합니다.
  2. 표준에 의해 요구되지 않습니다. 정의에 따르면 크기는 표현의 일부입니다. 크기가 다를 수 있다면 표현도 다를 수 있습니다.
  3. 반드시 그런 것은 아닙니다. 실제로, "아키텍처의 비트 길이"는 모호한 진술입니다. 64 비트 프로세서 란 무엇입니까? 주소 버스입니까? 레지스터의 크기? 데이터 버스? 뭐?
  4. 포인터를 "곱하기"또는 "나누는"것은 의미가 없습니다. 그것은 컴파일러에 의해 금지되어 있지만 물론 당신은 물론 근본적인 표현을 곱하거나 나눌 수 있으며 (실제로는 이해가되지 않음) 정의되지 않은 동작이 발생합니다.
  5. 어쩌면 나는 당신의 요점을 이해하지 못할 것입니다 모든 것 디지털 컴퓨터에서는 이진 번호입니다.
  6. 예; 거의. 그것은 sizeof(pointer_type) 더 멀리. 반드시 숫자의 산술 추가와 동등한 것은 아닙니다 (즉 더 멀리 여기서 논리적 개념입니다. 실제 표현은 아키텍처에 따라 다릅니다)

6의 경우 : 포인터가 반드시 메모리 주소는 아닙니다. 예를 들어 "참조"위대한 포인터 음모"스택 오버 플로우 사용자 JALF:

예, 위의 주석에서“주소”라는 단어를 사용했습니다. 내가 의미하는 바를 깨닫는 것이 중요합니다. 나는“데이터가 물리적으로 저장되는 메모리 주소”를 의미하는 것이 아니라 단순히“값을 찾기 위해 필요한 모든 것을 추상적입니다. 나는 주소가 무엇이든할지 모르지만 일단 우리가 그것을 가지고 있으면 우리는 항상 찾아서 수정할 수 있습니다. "

그리고:

포인터는 메모리 주소가 아닙니다! 위에서 언급했지만 다시 말합시다. 포인터는 일반적으로 컴파일러에 의해 단순히 메모리 주소로 구현되지만 그렇지 않아도됩니다. "

C99 표준의 포인터에 대한 추가 정보 :

  • 6.2.5 §27 void* 그리고 char* 동일한 표현이 있습니다. 즉, 변환없이 상호 교환 할 수 있습니다. 즉, 동일한 주소는 동일한 비트 패턴으로 표시됩니다 (다른 포인터 유형에 맞지 않음).
  • 6.3.2.3 §1은 불완전하거나 객체 유형에 대한 포인터가 캐스트 될 수 있다고 명시하고 있습니다. void* 그리고 다시 돌아와서 여전히 유효합니다. 여기에는 기능 포인터가 포함되지 않습니다!
  • 6.3.2.3 §6은 다음을 나타냅니다 void* 정수로 캐스트 할 수 있으며 7.18.1.4 §1은 적절한 유형을 제공합니다. intptr_t 그리고 uintptr_t; 문제 : 이러한 유형은 선택 사항입니다. 표준은 실제로 포인터의 값을 보유 할 수있을만큼 큰 정수 유형이 없다고 명시 적으로 언급합니다!

sizeof(char*) != sizeof(void(*)(void) ? - 36 비트 주소 지정 모드에서 X86에 있지 않음 (Pentium 1 이후 거의 모든 인텔 CPU에서 지원)

"포인터의 메모리 내 표현은 같은 비트 길이의 정수와 동일합니다."-현대 아키텍처에는 메모리 내 표현이 없습니다. 태그 메모리는 C에 걸리지 않았으며 C가 표준화되기 전에 이미 쓸모 없었다. 실제로 메모리는 정수를 붙잡고 비트와 틀림없이 단어를 가지고 있지 않습니다 (바이트가 아님; 대부분의 물리적 메모리는 8 비트 만 읽을 수 없습니다.)

"포인터의 곱셈은 불가능하다" - 68000 가족; 주소 레지스터 (보유 포인터)는 해당 IIRC를 지원하지 않았습니다.

"모든 포인터는 정수로 캐스트 될 수 있습니다" - 사진은 아닙니다.

"t*를 증가시키는 것은 메모리 주소에 (t) 크기를 추가하는 것과 같습니다." - 정의에 따라 true. 또한 동일합니다 &pointer[1].

나는 다른 사람들에 대해 알지 못하지만 DOS의 경우 #3의 가정은 사실이 아닙니다. DOS는 16 비트이며 다양한 트릭을 사용하여 16 비트 이상의 메모리를 매핑합니다.

포인터의 메모리 표현은 아키텍처와 같은 비트 길이의 정수와 동일합니다.

예를 들어, 80186에서, 32 비트 포인터는 두 개의 레지스터 (오프셋 레지스터 A 세그먼트 레지스터)로 유지되기 때문에이 가정은 거짓이라고 생각합니다 (오프셋 레지스터 A 세그먼트 레지스터).

포인터 데이터 유형의 곱셈 및 분할은 컴파일러에 의해서만 금지됩니다.

유형을 곱하거나 나눌 수 없습니다. ;피

왜 포인터를 곱하거나 나누고 싶은지 잘 모르겠습니다.

모든 포인터 값은 단일 정수에 캐스트 될 수 있습니다. 다시 말해, 여전히 세그먼트와 오프셋을 사용하는 아키텍처는 무엇입니까?

C99 표준을 사용하면 포인터가 저장 될 수 있습니다 intptr_t, 정수 유형입니다. 그래서 그렇습니다.

포인터를 증가시키는 것은 포인터에 의해 저장된 메모리 주소에 크기를 추가하는 것과 같습니다. P가 int32* 인 경우 p+1은 p 이후 메모리 주소 4 바이트와 같습니다.

x + y 어디 x a T * 그리고 y 정수는 동등합니다 (T *)((intptr_t)x + y * sizeof(T)) 내가 아는 한. 정렬이 문제 일 수 있지만 패딩이 제공 될 수 있습니다. sizeof. 정말 확실하지 않습니다.

일반적으로 모든 질문에 대한 답은 ""그리고 인기있는 언어를 구현하는 기계만이 날의 빛을 직접보고 현재 세기에 지속 되었기 때문입니다. 언어 표준은 이러한"불변 "또는 주장을 변화시킬 권리가 있지만 실제로는 일어나지 않았습니다. 제품 3과 4를 제외하고는 보편적으로 진실되기 위해 약간의 휴식이 필요합니다.

지난 몇 년 동안 학문적으로 인기있는 기능 기반 아키텍처와 대략적으로 일치하는 세그먼트 화 된 MMU 설계를 구축 할 수는 있지만, 그러한 시스템은 일반적으로 이러한 기능을 사용하여 일반적으로 사용되지 않았습니다. 그러한 시스템은 아마도 큰 포인터가 있었을 때 주장과 충돌했을 수 있습니다.

종종 큰 포인터가있는 세그먼트/기능 MMU 외에도 더 극단적 인 디자인은 포인터의 데이터 유형을 인코딩하려고 시도했습니다. 이들 중 일부는 지금까지 지어졌습니다. (이 질문은 기본 단어 지향적 인 포드 아키텍처에 대한 모든 대안을 제시합니다.)

구체적으로:

  1. 주어진 아키텍처에 대한 모든 포인터의 메모리 표현은 지적 된 데이터 유형에 관계없이 동일합니다. 강력한 언어가 아니라 하드웨어로 보호를 구현하려고 시도한 매우 엉뚱한 과거 디자인을 제외하고는 사실입니다.
  2. 포인터의 메모리 표현은 아키텍처와 같은 비트 길이의 정수와 동일합니다. 어쩌면 확실히 어떤 종류의 적분 유형이 동일합니다. LP64 대 LLP64.
  3. 포인터 데이터 유형의 곱셈 및 분할은 컴파일러에 의해서만 금지됩니다. 오른쪽.
  4. 모든 포인터 값은 단일 정수에 캐스트 될 수 있습니다. 다시 말해, 여전히 세그먼트와 오프셋을 사용하는 아키텍처는 무엇입니까? 오늘날 세그먼트와 오프셋을 사용하는 것은 없지만 C int 종종 충분히 크지 않습니다. long 또는 long long 포인터를 잡고 있습니다.
  5. 포인터를 증가시키는 것은 포인터에 의해 저장된 메모리 주소에 크기를 추가하는 것과 같습니다. P가 int32* 인 경우 p+1은 p 이후 메모리 주소 4 바이트와 같습니다. 예.

모든 인텔 아키텍처 CPU (즉, 모든 단일 Peecee)에는 서사시적이고 전설적인 복잡성의 정교한 세그먼테이션 유닛이 포함되어 있다는 점에 주목하는 것은 흥미 롭습니다. 그러나 효과적으로 비활성화됩니다. PC OS가 부팅 될 때마다 세그먼트베이스를 0으로 설정하고 세그먼트 길이를 ~ 0으로 설정하고 세그먼트를 무효화하고 평평한 메모리 모델을 제공합니다.

1950 년대, 1960 년대 및 1970 년대에는 많은 "단어가 다루어진 단어"건축이있었습니다. 그러나 C 컴파일러가있는 주류 예제를 기억할 수 없습니다. 나는 기억한다 ICL / Three Rivers Perq 기계 1980 년대에는 단어가 다루어졌으며 쓰기 가능한 제어점 (마이크로 코드)이있었습니다. 인스턴스베이션 중 하나는 C 컴파일러와 유닉스의 맛이 호출되었습니다. PNX, 그러나 C 컴파일러에는 특수 마이크로 코드가 필요했습니다.

기본 문제는 단어 주소 지정된 기계의 숯* 유형이 어색하지만 구현한다는 것입니다. 당신은 종종 sizeof(int *) != sizeof(char *) ...

흥미롭게도 C 이전에는 언어가있었습니다 BCPL 기본 포인터 유형은 단어 주소였다. 즉, 포인터를 늘리면 다음 단어의 주소를 주었고 ptr!1 당신에게 단어를 주었다 ptr + 1. 바이트를 해결하기위한 다른 연산자가있었습니다. ptr%42 내가 기억한다면.

편집 : 혈당이 낮을 때 질문에 대답하지 마십시오. 당신의 두뇌 (확실히, 내)는 당신이 예상대로 작동하지 않습니다. :-(

사소한 nitpick :

P는 int32*이 다음 p+1입니다

잘못된 경우, 서명되지 않은 INT32가 필요합니다. 그렇지 않으면 2GB로 랩핑됩니다.

흥미로운 이상 - 트랜스 퍼 칩의 C 컴파일러 저자로부터 이것을 얻었습니다. 그는 그 컴파일러에 대해 NULL이 -2GB로 정의되었다고 말했습니다. 왜요? 트랜스 퍼터에는 서명 된 주소 범위가 있었기 때문에 -2GB ~ +2GB. 당신은 그것을 믿을 수 있습니까? 놀랍지 않습니까?

나는 그 이후로 여러 사람들을 만났다. 나는 동의하지만, 당신이하지 않으면 당신이 당신의 주소 범위의 중간에있는 null 포인터를 끝내게됩니다.

나는 우리 대부분이 우리가 전출기를하고 있지 않아서 기뻐할 수 있다고 생각합니다!

아래에 나열된 가정을 위반하는 아키텍처를 알고 싶습니다.

Stephen C가 PERQ 시스템을 언급했고 MSalters가 68000 및 PIC를 언급한 것을 확인했습니다.

나는 다른 누구도 실제로 부당한 가정에 맞지 않는 표준 호환 C 컴파일러를 갖춘 이상하고 멋진 아키텍처의 이름을 지정하여 질문에 대답하지 않았다는 사실에 실망했습니다.

sizeof (int *) == sizeof (char *) == sizeof (void *) == sizeof (func_ptr *)?

반드시 그런 것은 아닙니다.몇 가지 예:

Harvard-Arachitecture 8 비트 프로세서 (PIC 및 8051 및 M8C)의 대부분의 컴파일러는 크기 (int *) == sizeof (char *)를 만들지 만 크기의 크기 (func_ptr *)와 다릅니다.

해당 제품군의 매우 작은 칩 중 일부는 256바이트 RAM(또는 그 이하)을 갖지만 몇 킬로바이트의 PROGMEM(플래시 또는 ROM)을 갖습니다. 따라서 컴파일러는 종종 sizeof(int *) == sizeof(char *)를 1과 동일하게 만듭니다(a 단일 8비트 바이트), sizeof(func_ptr *)는 2(2개의 8비트 바이트)와 같습니다.

RAM이 몇 킬로바이트이고 PROGMEM이 128킬로바이트 정도인 제품군의 많은 대형 칩용 컴파일러는 sizeof(int *) == sizeof(char *)를 2(2개의 8비트 바이트)와 동일하게 만들지 만 sizeof( func_ptr *)는 3(3개의 8비트 바이트)과 같습니다.

몇몇 Harvard 아키텍처 칩은 정확히 2^16("64KByte")의 PROGMEM(플래시 또는 ROM) 전체를 저장할 수 있고 또 다른 2^16("64KByte")의 RAM + 메모리 매핑 I/O를 저장할 수 있습니다.이러한 칩의 컴파일러는 sizeof(func_ptr *)를 항상 2(2바이트)로 만듭니다.그러나 종종 다른 종류의 포인터 sizeof(int *) == sizeof(char *) == sizeof(void *) 를 "긴 ptr"로 만드는 방법이 있습니다. 3바이트 일반 포인터 포인터가 RAM을 가리키는지 아니면 PROGMEM을 가리키는지 나타내는 추가 매직 비트가 있습니다.(이것은 다양한 서브루틴에서 해당 함수를 호출할 때 "print_text_to_the_LCD()" 함수에 전달해야 하는 일종의 포인터입니다. 때로는 RAM의 어느 곳에나 있을 수 있는 버퍼의 변수 문자열 주소를 사용하거나 다른 경우에는 PROGMEM 어디에나 있을 수 있는 많은 상수 문자열 중 하나입니다.이러한 컴파일러에는 프로그래머가 동일한 프로그램에서 세 가지 다른 종류의 char 포인터(어디를 나타내는 데 2바이트만 필요한 상수 문자열)를 구체적으로 표시할 수 있도록 특수 키워드("short" 또는 "near", "long" 또는 "far")가 있는 경우가 많습니다. PROGMEM에서는 RAM의 위치를 ​​나타내는 데 2바이트만 필요한 상수가 아닌 문자열과 "print_text_to_the_LCD()"가 허용하는 종류의 3바이트 포인터가 있습니다.

1950년대와 1960년대에 제작된 대부분의 컴퓨터는 36비트 워드 길이 또는 18비트 워드 길이, 18비트(또는 그 이하) 주소 버스를 사용합니다.그런 컴퓨터를 위한 C 컴파일러에서는 자주 사용한다고 들었습니다. 9비트 바이트, sizeof (int *) == sizeof (func_ptr *) = 2는 18 비트를 제공합니다. 모든 정수와 함수는 단어 정렬되어야하기 때문입니다.그러나 sizeof(char *) == sizeof(void *) == 4를 활용하려면 특수 PDP-10 지침 이러한 포인터를 전체 36비트 단어로 저장합니다.전체 36비트 워드에는 18비트 워드 주소와 해당 워드 내에서 가리키는 문자의 비트 위치를 나타내는 다른 18비트의 몇 가지 추가 비트가 포함됩니다.

주어진 아키텍처에 대한 모든 포인터의 메모리 표현은 지적 된 데이터 유형에 관계없이 동일합니까?

반드시 그런 것은 아닙니다.몇 가지 예:

위에서 언급한 아키텍처 중 하나에서 포인터의 크기는 다양합니다.그렇다면 어떻게 "동일한" 표현을 가질 수 있을까요?

일부 시스템의 일부 컴파일러는 다음을 사용합니다. "설명자" 문자 포인터와 다른 종류의 포인터를 구현합니다.그러한 설명자는 다른 "의 첫 번째 "문자"를 가리키는 포인터의 경우char big_array[4000]"의 첫 번째 "문자"를 가리키는 포인터보다 "char small_array[10]"는 작은 배열이 이전에 큰 배열이 차지했던 메모리의 정확히 동일한 위치에서 시작하더라도 틀림없이 다른 데이터 유형입니다.설명자를 사용하면 이러한 시스템이 다른 시스템에서 이러한 문제를 일으키는 버퍼 오버플로를 포착하고 트랩할 수 있습니다.

그만큼 "저지방 포인터" SAFElite 및 유사한 "소프트 프로세서"에 사용되는 포인터가 가리키는 버퍼의 크기에 대한 유사한 "추가 정보"가 있습니다.저지방 포인터는 버퍼 오버플로를 포착하고 트래핑하는 동일한 이점을 갖습니다.

포인터의 메모리 표현은 아키텍처와 같은 비트 길이의 정수와 동일합니까?

반드시 그런 것은 아닙니다.몇 가지 예:

~ 안에 "태그된 아키텍처" 기계에서는 메모리의 각 단어에는 해당 단어가 정수인지 포인터인지 또는 다른 것인지를 나타내는 비트가 있습니다.이러한 기계에서는 태그 비트를 보면 해당 단어가 정수인지 포인터인지 알 수 있습니다.

Nova 미니컴퓨터에는 "간접 비트" 영감을 주는 단어 하나하나에 "간접 스레드 코드".정수를 저장하면 해당 비트가 지워지고 포인터를 저장하면 해당 비트가 설정되는 것처럼 들립니다.

포인터 데이터 유형의 곱셈 및 분할은 컴파일러에 의해서만 금지됩니다.메모:예, 이것이 말도 안되는 일이라는 것을 알고 있습니다.내 말은 -이 잘못된 사용을 금지하는 하드웨어 지원이 있습니까?

예, 일부 하드웨어는 이러한 작업을 직접 지원하지 않습니다.

다른 사람들이 이미 언급했듯이 68000 및 6809의 "곱하기" 명령은 (일부) "데이터 레지스터"에서만 작동합니다."주소 레지스터"의 값에 직접 적용할 수 없습니다.(컴파일러가 이러한 제한 사항을 해결하는 것은 매우 쉬울 것입니다. 즉, 해당 값을 주소 레지스터에서 적절한 데이터 레지스터로 이동한 다음 MUL을 사용하는 것입니다.)

모든 포인터 값을 단일 데이터 유형으로 캐스팅할 수 있습니까?

예.

위해서는 memcpy()가 제대로 작동하려면, C 표준에서는 모든 종류의 모든 포인터 값을 void 포인터("void *")로 캐스팅할 수 있도록 요구합니다.

세그먼트와 오프셋을 계속 사용하는 아키텍처의 경우에도 이 작업을 수행하려면 컴파일러가 필요합니다.

모든 포인터 값을 단일 정수로 캐스팅할 수 있나요?다시 말해, 어떤 아키텍처가 여전히 세그먼트와 오프셋을 사용 하는가?

잘 모르겠습니다.

나는 모든 포인터 값이 "에 정의된 "size_t" 및 "ptrdiff_t" 통합 데이터 유형으로 캐스팅될 수 있다고 생각합니다.<stddef.h>".

포인터를 증가시키는 것은 포인터에 의해 저장된 메모리 주소에 크기를 추가하는 것과 같습니다.P가 int32* 인 경우 p+1은 p 이후 메모리 주소 4 바이트와 같습니다.

여기서 무엇을 요구하고 있는지 불분명합니다.

큐:어떤 종류의 구조나 기본 데이터 유형의 배열이 있는 경우(예: "#include <stdint.h> ... int32_t example_array[1000]; ..."), 해당 배열을 가리키는 포인터를 증가시킵니다(예: "int32_t p = &example_array[99];...p++;..."), 이제 포인터가 해당 배열의 바로 다음 연속 멤버를 가리키고 있습니까? 이 멤버는 메모리에서 더 먼 sizeof(지정된 데이터 유형) 바이트입니다.

ㅏ:예, 컴파일러는 표준을 준수하기 위해 포인터를 한 번 증가시킨 후 배열의 다음 독립 연속 int32_t, 메모리에서 sizeof(지정된 데이터 유형) 바이트를 더 가리키는 포인터를 만들어야 합니다.

큐:따라서 p가 int32*라면 p+1은 p 뒤의 4바이트 메모리 주소와 동일합니까?

ㅏ:sizeof( int32_t )가 실제로 4와 같으면 그렇습니다.그렇지 않으면 sizeof( int32_t )가 2 또는 심지어 1일 수 있는 일부 최신 DSP를 포함하는 특정 단어 주소 지정 가능 기계의 경우 p+1은 p 이후의 메모리 주소 2 또는 심지어 1 "C 바이트"와 같습니다.

큐:그래서 포인터를 가져와서 "int"로 캐스팅하면...

ㅏ:"전 세계는 VAX 이단"의 한 유형입니다.

큐:...그런 다음 해당 "int"를 다시 포인터로 캐스팅합니다.

ㅏ:"전 세계는 VAX 이단"의 또 다른 유형입니다.

큐:따라서 int32_t에 대한 포인터인 포인터 p를 가져와서 포인터를 포함할 만큼 충분히 큰 일부 정수 유형으로 캐스팅한 다음 추가하면 sizeof( int32_t ) 해당 정수 유형으로 변환한 다음 나중에 해당 정수 유형을 포인터로 다시 캐스팅합니다. 이 모든 작업을 수행하면 결과 포인터가 p+1이 될까요?

반드시 그런 것은 아닙니다.

많은 DSP와 몇몇 다른 최신 칩에는 8비트 칩에서 사용되는 바이트 중심 처리가 아닌 단어 중심 주소 지정 기능이 있습니다.

이러한 칩을 위한 일부 C 컴파일러는 각 단어에 2개의 문자를 넣지만 int32_t를 보유하려면 2개의 단어가 필요합니다. 그래서 그들은 다음과 같이 보고합니다. sizeof( int32_t ) 4입니다.(저는 C 컴파일러가 있다는 소문을 들었습니다. 24비트 이를 수행하는 Motorola 56000).

컴파일러는 int32_t에 대한 포인터로 "p++"를 수행하면 다음 int32_t 값에 대한 포인터가 증가하도록 배열해야 합니다.컴파일러가 이를 수행하는 방법에는 여러 가지가 있습니다.

표준을 준수하는 한 가지 방법은 int32_t에 대한 각 포인터를 "기본 단어 주소"로 저장하는 것입니다.단일 int32_t 값을 보유하려면 2개의 단어가 필요하기 때문에 C 컴파일러는 "int32_t * p; ... p++"를 해당 포인터 값을 2씩 증가시키는 일부 어셈블리 언어로 변환합니다.반면에 만약 그 사람이 "int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;", 56000용 C 컴파일러는 포인터 값을 4씩 증가시키는 어셈블리 언어로 컴파일할 가능성이 높습니다.

나는 인접한 가상 메모리 공간에서 사용되는 포인터에 가장 많이 익숙합니다.

여러 PIC 및 8086 및 기타 시스템에는 "하드웨어를 더 간단하게 만든"주소로 몇 개의 RAM 블록이 발생하지 않습니다.메모리 매핑된 I/O가 있거나 해당 블록 사이의 주소 공간 간격에 아무것도 연결되지 않은 경우입니다.

들리는 것보다 훨씬 더 어색합니다.

어떤 경우에는 - 비트 밴딩 하드웨어 로 인해 발생하는 문제를 방지하기 위해 사용됩니다. 읽기-수정-쓰기 -- RAM의 정확히 동일한 비트는 2개 이상의 서로 다른 주소를 사용하여 읽거나 쓸 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top