2

따라서 this answerMichael Burr 님의 의견을 알게 된 C 표준은 배열의 첫 번째 요소를 지나간 포인터에서 정수 빼기를 지원하지 않습니다. 할당 된 메모리가 포함되어 있다고 가정합니다. the combined C99 + TC1 + TC2 (PDF)의 섹션 6.5.6에서어떤 C 컴파일러가 포인터 빼기를 언더 플로우합니까?

:

경우 포인터 피연산자와 같은 배열 객체의 요소에 따라서 포인트 또는 배열 객체의 마지막 요소는 최근 1 둘 다 평가는 과잉을 일으키지 않아야한다. 그렇지 않으면 동작이 정의되지 않습니다.

저는 포인터 연산을 좋아하지만, 전에 걱정했던 적이 없었습니다. 나는 항상 주어진 것으로 가정했습니다

int a[1]; 
int * b = a - 3; 
int * c = b + 3; 

c == a있다.

내가 이전에 그런 종류의 일을하고 물지 않았다고 믿는 동안, 내가 함께해온 다양한 컴파일러의 친절 함 때문일 것임에 틀림 없다. 표준은 내가 생각한대로 포인터 산술 연산을해야합니다.

제 질문은 일반적인 내용입니다. 저에게 친절한 컴파일러가 있습니까? 포인터의 산술 연산이 배열의 경계를 벗어나는 표준입니까?

+3

CPU 아키텍처와 관련하여 컴파일러의 문제는 그리 많지 않습니다. 거기에 몇 가지 모호한 메모리 모델이 있습니다, 당신은 일반적으로 모든 시스템에서 일반 선형 메모리를 가정 수 없습니다. 포인터를 메모리 주소로 생각하지 마십시오. 그들은 아니다. 그들에는 분리 된 세트가있다. – jalf

답변

4

이것은 표준에 의해 "구현이 정의 된"것이 아니며, 이것은 표준에 의해 "정의되지 않음"입니다. 즉,이를 지원하는 컴파일러에 의존 할 수 없다는 것을 의미합니다. "이 코드는 컴파일러 X에서 안전합니다"라고 말할 수는 없습니다. 정의되지 않은 동작을 호출하면 프로그램이 정의되지 않습니다.

실용적인 대답은 "어떻게 (언제, 어떤 컴파일러에서)이 문제를 해결할 수 있습니까?"입니다. 실질적인 대답은 "하지 마라"입니다.

+0

나는 OP가 * 왜 * 이것이 무엇이든간에 사실인지 궁금해하고 있다고 생각합니다. Windows 3.0 용 응용 프로그램을 개발할 때 "기쁨"을 경험 한 적이 없다면 오늘 얼마나 쉽게 사용할 수 있는지 이해하지 못할 수도 있습니다. – RBerteig

+0

실제로 Windows 3.0 용 프로그램을 작성했습니다. 당시 파일 관리자는 하나의 파일 유형 만 하나의 프로그램과 연관시킬 수있었습니다. 사용자가 파일 형식마다 여러 프로그램을 추가 할 수 있도록 처리기를 작성했습니다. 그런 다음 사용자는 해당 파일을 해당 프로그램과 연관 시켰습니다.이 프로그램을 마우스 오른쪽 버튼으로 클릭하면 사용자가 해당 파일 유형에 대한 프로그램의 관세 목록에서 선택할 수있었습니다. – tpdi

+1

MSC 컴파일러를 DOS 상자에서 안정적으로 실행할 수 없으므로 컴파일하기 위해 DOS로 돌아가는 것을 기억합니다. 게다가 어떤 버그가 있은 후에도 Windows를 종료하려면 3 손가락 경례가 필요했고 그 다음에 DOS 프롬프트가 처음이었습니다 ... 실제 기쁨은 종이와 텍스트 편집기에서 대화 상자 레이아웃을 디자인하고 그들이 닮은 것을보기 위해 컴파일을 통해 앉아서 ... – RBerteig

7

MSDOS FAR 포인터에는 세그먼트 레지스터와 리얼 모드의 겹침 부분을 "똑똑하게"사용하여 덮어 쓴 것과 같은 문제가있었습니다. 그 결과, 16 비트 세그먼트는 왼쪽으로 4 비트 이동 한 것이고 16 비트 오프셋에 추가되어 1MB를 처리 할 수있는 20 비트의 물리적 주소를 부여했습니다. 이는 아무도 필요로하지 않는다는 것을 모두 알고 있기 때문에 충분했습니다. 640KB의 RAM과 같습니다. ;-)

보호 모드에서 세그먼트 레지스터는 실제로 메모리 설명자 테이블의 인덱스였습니다. 일반적인 DOS 확장 런타임은 일반적으로 실제 모드에서와 같이 많은 세그먼트를 처리 할 수 ​​있도록 일을 정렬하므로 실제 모드의 이식 코드를 쉽게 만들 수 있습니다. 그러나 그것은 약간의 결함이있었습니다. 주로 의 앞에 할당은 할당의 일부가 아니므로 해당 설명자가 유효하지 않을 수도 있습니다.

보호 모드의 80286에서 잘못된 설명자를로드하는 값으로 세그먼트 레지스터를로드하면 설명자가 실제로 메모리를 참조하는 데 사용되었는지 여부에 관계없이 예외가 발생합니다.

비슷한 문제가 잠재적으로 할당 이후 1 바이트에서 발생합니다. 포인터의 마지막 ++가 세그먼트 레지스터로 옮겨져 새로운 디스크립터를로드 할 수 있습니다. 이 경우 메모리 할당자가 안전한 디스크립터를 할당 된 범위의 끝을지나도록 배열 할 수 있다고 예상하는 것이 합리적입니다. 그러나 그 이상으로 배열 할 것을 기대하는 것은 무리 일 수 있습니다.

+0

http://www.faktoider.nu/640kb_eng.html :) – unwind

+0

고마워 ... 내가 문구를 인용하지 않았 음을 알게 될 것이다. 그것을 위해 Bill을 탓해야합니다. 심각하게 생각하는 사람은 없지만 큰 기억을 위해 * 지불해야하는 사람에게는 그 태도가 생소하지 않습니다 .... – RBerteig

0

ZETA-C (TI 탐색기 용) 포인터는 배열 및 인덱스 또는 대체 된 배열 인 IIRC로 구현되므로 예제가 제대로 작동하지 않을 수 있습니다. 어떤 동작이 될지 파악하려면 zcprim>pointer-subtract에서 zcprim.lisp으로 시작하십시오. 이것이 표준에 맞는지는 알 수 없지만 나는 그 느낌이 들었습니다.

1

또 다른 이유는 포인터가 항상 할당 된 범위 안에 있다고 가정하는 임의의 보수적 인 가비지 수집기 (예 : boehm-weiser GC)가 있고, 그렇지 않은 경우 언제든지 메모리를 해제 할 수 있다는 것입니다.

이 가정을 깨뜨리는 인기있는 상업용 품질 라이브러리 및 중고 라이브러리가 있으며 매우 복잡한 해시 구조를 구현하기 위해 포인터 알고리즘을 사용하는 HP의 Judy Trees Library입니다.