Null 포인터에 주소 0이 사용되는 이유는 무엇입니까?
C(혹은 C++)에서 포인터는 값이 0인 경우 특별합니다.메모리를 해방한 후 포인터를 0으로 설정하는 것이 좋습니다.이것은 포인터를 해방하는 것이 위험하지 않다는 것을 의미하기 때문입니다.메모리를 취득할 수 없는 경우는, 값이 0인 포인터를 반환합니다.if (p != 0)
항상 패스한 포인터가 유효한지 확인하는 등
하지만 메모리 주소 지정은 0부터 시작되므로 0도 다른 주소와 마찬가지로 유효한 주소 아닌가요?이 경우 null 포인터 처리에 0을 어떻게 사용할 수 있습니까?왜 음수가 대신 null이 아닐까요?
편집:
좋은 답변들이 많네요.내 마음이 해석하는 대로 답변에 대해 정리하고, 내가 오해하면 커뮤니티에서 바로잡아 주길 바란다.
프로그래밍의 다른 모든 것과 마찬가지로 그것은 추상화이다.주소 0과 실제로 관련이 없는 상수입니다.C++0x는 키워드를 추가하여 이를 강조합니다.
nullptr
.주소 추상화도 아니고, C 표준에 의해 지정된 상수이며, 컴파일러는 "실제" 주소와 동일하지 않은 경우 다른 번호로 변환할 수 있습니다.또, 0이 플랫폼에 최적인 값이 아닌 경우, 다른 늘 포인터와 동등합니다.
초기처럼 추상화가 아닌 경우 0번 주소는 시스템에 의해 사용되며 프로그래머에게는 제한되지 않습니다.
제가 부정적인 숫자를 제안했던 건 좀 무모한 브레인스토밍이었어요, 인정해요.주소에 부호 있는 정수를 사용하는 것은 null 포인터(-1 또는 기타)를 제외하고 값 공간이 유효한 주소를 만드는 양의 정수와 낭비되는 음수 사이에 균등하게 분할된다는 것을 의미할 경우 다소 낭비됩니다.
데이터 타입으로 항상 나타낼 수 있는 숫자는 0입니다(아마도 1도 마찬가지입니다).1비트 정수는 부호 없는 경우 0 또는 1이 되거나 부호 있는 경우만 되거나 [-2, 1]이 되는 2비트 정수를 생각할 수 있습니다.다만, 0이 늘이고, 1이 메모리에서 액세스 가능한 유일한 바이트로 할 수 있습니다.)
아직도 내 마음속에 해결되지 않은 것이 있다.스택 오버플로우 질문 특정 고정 주소에 대한 포인터에서는 늘 포인터의 0이 추상화일지라도 다른 포인터 값은 반드시 추상화되지 않음을 알 수 있습니다.이렇게 하면 스택 오버플로우 질문을 다시 올릴 수 있습니다.주소 0에 액세스 할 수 있을까요?
2점:
소스 코드의 상수 값 0만이 늘 포인터입니다.컴파일러 실장은 실행 코드에서 원하는 값 또는 필요한 값을 모두 사용할 수 있습니다.일부 플랫폼에는 구현에서 늘 포인터로 사용할 수 있는 '잘못된' 특별한 포인터 값이 있습니다.C FAQ에는 "진지하게, 실제 머신이 제로 이외의 늘 포인터 또는 다른 타입의 포인터에 대해 다른 표현을 사용한 적이 있는가?"라는 질문이 있습니다.이러한 질문은 실행 시 C 소스의 늘 포인터로서 이 속성0을 사용한 여러 플랫폼을 지적합니다.C++ 규격에는 "값이 0인 정수식을 변환하면 항상 늘 포인터가 생성되지만 값이 0인 다른 식을 변환하면 늘 포인터가 생성되지 않아도 됩니다."라고 명시되어 있습니다.
음의 값은 플랫폼이 주소와 마찬가지로 사용할 수 있습니다.C 표준은 단순히 늘 포인터를 나타내기 위해 사용할 것을 선택하면 0이 선택됩니다.솔직히 다른 초병 값을 고려했는지 잘 모르겠어요.
null 포인터의 요건은 다음과 같습니다.
- 실제 물체에 대한 포인터와 비교할 수 없습니다.
- 임의의 2개의 늘포인터가 동등하게 비교된다(C++는 같은 타입의 포인터에 대해서만 유지되도록 이것을 조정한다).
지금까지 0부터 시작하는 주소 공간은 항상 ROM이었고 일부 운영체제시스템 또는 로우레벨 인터럽트 처리 루틴에 사용되었습니다.현재는 모든 것이 가상(주소 공간 포함)이기 때문에 운영체제는 임의의 주소에 할당이 가능하기 때문에 주소 0에는 할당이 특별히 할당되지 않습니다.
IIRC, "null pointer" 값은 0이 될 수 없습니다.컴파일러는 0을 시스템에 적합한 임의의 "null" 값으로 변환합니다(실제로는 항상 0이지만 반드시 0일 필요는 없습니다).포인터를 0과 비교할 때마다 동일한 변환이 적용됩니다.포인터를 비교할 수 있는 것은 서로 및 이 special-value-0뿐이므로 프로그래머가 시스템의 메모리 표현에 대해 아무것도 알 수 없게 됩니다.왜 42나 somesuch가 아닌 0을 선택했는지에 대해서는 대부분의 프로그래머가 0부터 카운트하기 때문이라고 추측합니다.(또한 대부분의 시스템에서는 0이 첫 번째 메모리 주소이기 때문에 편리하기를 원했습니다.실제로 제가 설명한 것과 같은 번역은 거의 이루어지지 않기 때문입니다.언어는 이들을 위해 허용되기 때문입니다.)
포인터 컨텍스트에서 상수 0의 의미를 잘못 알고 있는 것 같습니다.
C에서도 C++ 포인터에서도 값 0을 가질 수 없습니다.포인터는 산술 개체가 아닙니다."0"이나 "음수"와 같은 숫자 값을 가질 수 없습니다.그래서 당신이 말한 "점수..."0의 가치를 가지고 있다"는 것은 전혀 의미가 없습니다.
C&C++ 포인터는 예약된 늘포인트 값을 가질 수 있습니다.null-pointer 값의 실제 표현은 "zeros"와는 아무런 관련이 없습니다.특정 플랫폼에 적합한 모든 것을 사용할 수 있습니다.대부분의 플랩에서 늘 포인트 값은 실제 제로 주소 값으로 물리적으로 표현됩니다.단, 일부 플랫폼주소 0이 실제로 어떤 목적으로 사용되고 있는 경우(주소0 에 오브젝트를 작성할 필요가 있는 경우 등), 이러한 플랫폼의 늘 포인트 값은 대부분 다릅니다.그것은 물리적으로 표현될 수 있다.0xFFFFFFFF
값 또는 로서 주소 지정0xBAADBAAD
예를 들어 주소 값입니다.
단, 특정 플랫폼에서 null-pointer 값이 어떻게 응답되는지에 관계없이 코드에서는 계속 null-pointer를 상수로 지정합니다.0
주어진 포인터에 늘 포인터 값을 할당하려면 다음과 같은 식을 계속 사용합니다.p = 0
컴파일러의 책임은 사용자가 원하는 것을 실현하고 그것을 적절한 늘 포인트 값 표현으로 변환하는 것, 즉 주소 값을 넣는 코드로 변환하는 것입니다.0xFFFFFFFF
포인터에p
,예를들면.
요컨대, 당신이 사용한다는 사실은0
마법사 코드에서 null-module 값을 생성하는 것은 null-module 값이 어떤 식으로든 주소에 연결되어 있음을 의미하지 않습니다.0
.그0
소스 코드에서 사용하는 것은 null-module 값이 "signal"인 실제 물리적 주소와는 전혀 관련이 없는 "구문적 설탕"일 뿐입니다.
하지만 메모리 주소 지정은 0부터 시작되므로 0도 다른 주소와 마찬가지로 유효한 주소 아닌가요?
일부/다수/모든 운영 체제에서 메모리 주소 0은 어떤 면에서 특별합니다.예를 들어 유효하지 않거나 존재하지 않는 메모리에 매핑되는 경우가 많습니다.이 경우 액세스하려고 하면 예외가 발생합니다.
왜 음수가 대신 null이 아닐까요?
포인터 값은 보통 부호 없는 수치로 취급됩니다.그렇지 않으면 32비트 포인터는 4GB가 아닌 2GB의 메모리밖에 주소를 지정할 수 없습니다.
매직 값 0이 잘못된 포인터를 정의하기 위해 선택되었을 것으로 추측됩니다.이는 더 적은 명령으로 테스트할 수 있기 때문입니다.일부 기계어는 레지스터를 로드할 때 데이터에 따라 제로 및 부호 플래그를 자동으로 설정하므로 별도의 비교 명령을 수행하지 않고도 간단한 로드로 늘 포인터를 테스트하고 분기 명령을 테스트할 수 있습니다.
(대부분의 ISA는 ALU 명령에만 플래그를 설정하고 로드는 설정하지 않습니다.그리고 보통 C 소스를 해석할 때 컴파일러를 사용하는 경우를 제외하고는 계산을 통해 포인터를 생성하지 않습니다.단, 적어도 비교할 임의의 포인터 폭 상수는 필요하지 않습니다.)
처음 작업한 컴퓨터인 Commodore Pet, Vic20, C64에서는 RAM이 Location 0에서 시작되었기 때문에 Null 포인터를 사용하여 읽고 쓸 수 있습니다.
그냥 관례인 것 같아요.잘못된 포인터를 표시하려면 값이 있어야 합니다.
주소 공간을 1바이트만 잃으면 문제가 되지 않습니다.
부정적인 포인트는 없습니다.포인터는 항상 부호가 없습니다.또, 음수가 되는 경우는, 주소 공간의 반을 잃게 됩니다.
C는 늘 포인터를 나타내기 위해 0을 사용하지만 포인터 자체는 0이 아닐 수 있습니다.그러나 대부분의 프로그래머는 늘 포인터가 사실상 0인 시스템만 사용합니다.
하지만 왜 0일까요?모든 시스템이 공유하는 하나의 주소입니다.또, 대부분의 경우, 낮은 주소는 operating system용으로 예약되어 있기 때문에, 그 값은 애플리케이션 프로그램에는 액세스 할 수 없습니다.포인터에 실수로 정수 값을 할당하면 다른 항목과 마찬가지로 0이 될 수 있습니다.
이전에는 응용 프로그램의 메모리 부족이 시스템 리소스에 의해 점유되었습니다.그 당시에는 0이 기본 null 값이 되었습니다.
최신 시스템에서는 이것이 반드시 해당되는 것은 아니지만, 포인터 값을 메모리 할당에 의해 전달된 값 이외의 값으로 설정하는 것은 여전히 좋지 않습니다.
삭제 후 포인터를 null로 설정하지 않고 나중에 "오류 노출"을 삭제한다는 인수에 대해...
만약 당신이 정말로 이것에 대해 걱정하고 있다면, 더 나은 접근법, 즉 확실하게 동작할 수 있는 방법은 assert()를 활용하는 것입니다.
...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...
이를 위해서는 몇 가지 추가 입력과 디버깅 빌드 중에 한 가지 추가 검사가 필요하지만 ptr이 '두 번' 삭제되었을 때 알림이 제공됩니다.코멘트 토론에서 제시된 다른 방법은 크래시가 발생하도록 포인터를 null로 설정하지 않는 것입니다.이것은 단순히 성공을 보증할 수 없습니다.더 나쁜 것은 위와 달리 이러한 "버그" 중 하나가 쉘프에 도달하면 사용자에게 크래시(또는 훨씬 더 나쁜)를 일으킬 수 있다는 것입니다.마지막으로 이 버전을 사용하면 프로그램을 계속 실행하여 실제로 무슨 일이 일어나는지 확인할 수 있습니다.
이것이 질문의 답변이 되지 않는 것은 알고 있습니다만, 코멘트를 읽은 누군가가, 포인터가 free()로 보내지거나 두 번 삭제될 가능성이 있는 경우는 0으로 설정하지 않는 것이 「좋은 관행」이라고 판단하지 않을까 걱정했습니다.이러한 몇 가지 경우 가능한 경우 정의되지 않은 동작을 디버깅 도구로 사용하는 것은 결코 좋은 방법이 아닙니다.유효하지 않은 포인터를 삭제하여 최종적으로 발생한 버그를 추적해야 했던 사람은 아무도 이것을 제안하지 않을 것입니다.이러한 종류의 오류는 찾아내는 데 몇 시간이 걸리고 원래 문제를 추적하는 것이 불가능한 전혀 예상치 못한 방식으로 프로그램에 거의 항상 영향을 미칩니다.
많은 운영체제가 늘 포인터 표현에 all-bits-zero를 사용하는 중요한 이유는 이것이 다음을 의미하기 때문입니다.memset(struct_with_pointers, 0, sizeof struct_with_pointers)
simply는 안에 있는 모든 포인터를 설정합니다.struct_with_pointers
null 포인터로 이동합니다.이것은 C 표준으로 보장되지 않지만, 많은 프로그램이 이를 가정합니다.
오래된 DEC 머신 중 하나(PDP-8, 내 생각에)에서는 C 런타임에 의해 메모리의 첫 페이지가 보호되므로 해당 블록 내의 메모리에 액세스하려고 하면 예외가 발생합니다.
The choice of sentinel value is arbitrary, and this is in fact being addressed by the next version of C++ (informally known as "C++0x", most likely to be known in the future as ISO C++ 2011) with the introduction of the keyword nullptr
to represent a null valued pointer. In C++, a value of 0 may be used as an initializing expression for any POD and for any object with a default constructor, and it has the special meaning of assigning the sentinel value in the case of a pointer initialization. As for why a negative value was not chosen, addresses usually range from 0 to 2N-1 for some value N. In other words, addresses are usually treated as unsigned values. If the maximum value were used as the sentinel value, then it would have to vary from system to system depending on the size of memory whereas 0 is always a representable address. It is also used for historical reasons, as memory address 0 was typically unusable in programs, and nowadays most OSs have parts of the kernel loaded into the lower page(s) of memory, and such pages are typically protected in such a way that if touched (dereferenced) by a program (save the kernel) will cause a fault.
It has to have some value. Obviously you don't want to step on values the user might legitimately want to use. I would speculate that since the C runtime provides the BSS segment for zero-initialized data, it makes a certain degree of sense to interpret zero as an un-initialized pointer value.
Rarely does an OS allow you to write to address 0. It's common to stick OS-specific stuff down in low memory; namely, IDTs, page tables, etc. (The tables have to be in RAM, and it's easier to stick them at the bottom than to try and determine where the top of RAM is.) And no OS in its right mind will let you edit system tables willy-nilly.
This may not have been on K&R's minds when they made C, but it (along with the fact that 0==null is pretty easy to remember) makes 0 a popular choice.
The value 0
is a special value that takes on various meanings in specific expressions. In the case of pointers, as has been pointed out many many times, it is used probably because at the time it was the most convenient way of saying "insert the default sentinel value here." As a constant expression, it does not have the same meaning as bitwise zero (i.e., all bits set to zero) in the context of a pointer expression. In C++, there are several types that do not have a bitwise zero representation of NULL
such as pointer member and pointer to member function.
Thankfully, C++0x has a new keyword for "expression that means a known invalid pointer that does not also map to bitwise zero for integral expressions": nullptr
. Although there are a few systems that you can target with C++ that allow dereferencing of address 0 without barfing, so programmer beware.
There are already a lot of good answers in this thread; there are probably many different reasons for preferring the value 0
for null pointers, but I'm going to add two more:
- In C++, zero-initializing a pointer will set it to null.
- On many processors it is more efficient to set a value to 0 or to test for it equal/not equal to 0 than for any other constant.
This is dependent on the implementation of pointers in C/C++. There is no specific reason why NULL is equivalent in assignments to a pointer.
There are historic reasons for this, but there are also optimization reasons for it.
It is common for the OS to provide a process with memory pages initialized to 0. If a program wants to interpret part of that memory page as a pointer then it is 0, so it is easy enough for the program to determine that that pointer is not initialized. (this doesn't work so well when applied to uninitialized flash pages)
Another reason is that on many many processors it is very very easy to test a value's equivalence to 0. It is sometimes a free comparison done without any extra instructions needed, and usually can be done without needing to provide a zero value in another register or as a literal in the instruction stream to compare to.
The cheap comparisons for most processors are the signed less than 0, and equal to 0. (signed greater than 0 and not equal to 0 are implied by both of these)
Since 1 value out of all of possible values needs to be reserved as bad or uninitialized then you might as well make it the one that has the cheapest test for equivalence to the bad value. This is also true for '\0' terminated character strings.
If you were to try to use greater or less than 0 for this purpose then you would end up chopping your range of addresses in half.
The constant 0
is used instead of NULL
because C was made by some cavemen trillions of years ago, NULL
, NIL
, ZIP
, or NADDA
would have all made much more sense than 0
.
But since memory addressing starts at 0, isn't 0 just as a valid address as any other?
Indeed. Although a lot of operating systems disallow you from mapping anything at address zero, even in a virtual address space (people realized C is an insecure language, and reflecting that null pointer dereference bugs are very common, decided to "fix" them by dissallowing the userspace code to map to page 0; Thus, if you call a callback but the callback pointer is NULL, you wont end up executing some arbitrary code).
How can 0 be used for handling null pointers if that is the case?
Because 0
used in comparison to a pointer will be replaced with some implementation specific value, which is the return value of malloc on a malloc failure.
Why isn't a negative number null instead?
This would be even more confusing.
ReferenceURL : https://stackoverflow.com/questions/2759845/why-is-address-zero-used-for-the-null-pointer
'programing' 카테고리의 다른 글
AM/PM과 함께 현재 시간을 12시간 형식으로 표시 (0) | 2022.05.30 |
---|---|
vue 구성 요소 데이터 내의 어레이를 업데이트하는 중 (0) | 2022.05.30 |
Vuex 돌연변이 및 작업이 작동하지 않음 (0) | 2022.05.30 |
리소스 사용 블록에서 여러 개의 연결된 리소스를 관리하기 위한 올바른 관용구입니까? (0) | 2022.05.30 |
구성 요소에서 vuex에 API 데이터 커밋 (0) | 2022.05.30 |