C 표준은 널 포인터가 기계의 주소 0에있을 것을 요구하지 않습니다. 그러나 0
상수를 포인터 값으로 캐스팅하면 NULL
포인터 (§6.3.2.3 / 3)가 생성되어야하며 null 포인터를 부울로 평가하는 것은 false 여야합니다. 이것은 당신이 정말로 경우 조금 어색 할 수 있습니다 않는 제로 주소를 원하고, NULL
제로 주소가 아닙니다.
그럼에도 불구하고 컴파일러 및 표준 라이브러리에 대한 (무거운) 수정으로 표준 라이브러리를 NULL
엄격하게 준수하면서 대체 비트 패턴으로 표현하는 것이 불가능하지 않습니다 . 그러나 자신 의 정의를 단순히 변경하는 것만으로 는 충분 하지 않습니다 .NULL
NULL
특히 다음을 수행해야합니다.
- 포인터에 대한 할당 (또는 포인터에 대한 캐스트)의 리터럴 0이
-1
.
0
대신 매직 값을 확인하기 위해 포인터와 상수 정수 간의 동등성 테스트를 정렬 합니다 (§6.5.9 / 6).
- 포인터 유형이 부울로 평가되는 모든 컨텍스트를 정렬하여 0을 확인하는 대신 매직 값과 같은지 확인합니다. 이것은 동등성 테스트 의미론을 따르지만 컴파일러는 내부적으로 다르게 구현할 수 있습니다. §6.5.13 / 3, §6.5.14 / 3, §6.5.15 / 4, §6.5.3.3 / 5, §6.8.4.1 / 2, §6.8.5 / 4 참조
- caf가 지적했듯이 정적 개체 (§6.7.8 / 10) 및 부분 복합 이니셜 라이저 (§6.7.8 / 21) 초기화에 대한 의미 체계를 업데이트하여 새로운 null 포인터 표현을 반영합니다.
- 실제 주소 0에 액세스하는 다른 방법을 만듭니다.
처리 할 필요가 없는 것들이 있습니다 . 예를 들면 :
int x = 0;
void *p = (void*)x;
그 후에 p
는 null 포인터가 보장되지 않습니다. 상수 할당 만 처리하면됩니다 (이는 실제 주소 0에 액세스하기위한 좋은 접근 방식입니다). 마찬가지로:
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
또한:
void *p = NULL;
int x = (int)p;
x
은 보장되지 않습니다 0
.
요컨대,이 조건은 C 언어위원회에 의해 분명히 고려되었으며 NULL에 대한 대체 표현을 선택하는 사람들을 위해 고려되었습니다. 지금해야 할 일은 컴파일러를 크게 변경하는 것뿐입니다.
참고로 컴파일러가 적절하기 전에 소스 코드 변환 단계를 통해 이러한 변경 사항을 구현할 수 있습니다. 즉, 전 처리기-> 컴파일러-> 어셈블러-> 링커의 일반적인 흐름 대신 전 처리기-> NULL 변환-> 컴파일러-> 어셈블러-> 링커를 추가합니다. 그런 다음 다음과 같은 변환을 수행 할 수 있습니다.
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
이를 위해서는 포인터에 해당하는 식별자를 결정하기 위해 유형 파서 및 typedef 및 변수 선언 분석뿐만 아니라 전체 C 파서가 필요합니다. 그러나 이렇게하면 컴파일러의 코드 생성 부분을 적절하게 변경하지 않아도됩니다. clang 은이를 구현하는 데 유용 할 수 있습니다. 이러한 변형을 염두에두고 설계 되었음을 이해합니다. 물론 표준 라이브러리도 변경해야 할 것입니다.
mprotect
보안 할 수있는 주소뿐입니다 . 또는 플랫폼에 ASLR 등이없는 경우 플랫폼 물리적 메모리를 초과하는 주소입니다. 행운을 빕니다.