널 포인터에 왜 주소 0이 사용됩니까?
C (또는 C ++)에서 포인터 값이 0이면 포인터가 특별합니다. 포인터를 해제 한 후 포인터를 0으로 설정하는 것이 좋습니다. 포인터를 다시 해제하는 것은 위험하지 않습니다. malloc을 호출하면 메모리를 얻을 수 없으면 값이 0 인 포인터를 반환합니다. if (p != 0)
전달 된 포인터가 유효한지 확인하기 위해 항상 사용 합니다.
그러나 메모리 주소 지정은 0에서 시작하기 때문에 0은 다른 주소만큼 유효하지 않습니까? 그렇다면 널 포인터를 처리하는 데 0을 어떻게 사용할 수 있습니까? 음수가 null이 아닌 이유는 무엇입니까?
편집하다:
좋은 답변이 많이 있습니다. 나는 내 마음이 그것을 해석 할 때 표현 된 답변에서 말한 것을 요약하고 내가 오해하면 공동체가 나를 바로 잡기를 희망한다.
프로그래밍의 다른 모든 것과 마찬가지로 추상화입니다. 주소 0과 관련이없는 상수 일뿐입니다. C ++ 0x는 키워드를 추가하여이를 강조합니다
nullptr
.주소 추상화도 아니고 C 표준에 의해 지정된 상수이며 컴파일러가 "실제"주소와 절대 같은지 확인하지 않는 한 다른 숫자로 변환 할 수 있으며 0이 0이 아닌 다른 널 포인터와 같은 경우 플랫폼에 가장 적합한 가치.
추상화가 아닌 경우 (초기의 경우), 주소 0은 시스템에서 사용되며 프로그래머에게는 한계를 벗어납니다.
내 음수 제안은 약간의 브레인 스토밍이었다고 인정한다. 주소에 부호있는 정수를 사용하는 것은 널 포인터 (-1 또는 그 밖의 것) 외에 값 공간이 유효한 주소를 만드는 양의 정수와 방금 낭비되는 음수로 균등하게 분할된다는 것을 의미하는 경우 약간 낭비입니다.
숫자가 항상 데이터 유형으로 표현 가능하면 0입니다. (아마도 1입니다. 부호없는 경우 0 또는 1 또는 부호있는 경우 부호있는 비트 또는 2 비트 정수 인 1 비트 정수를 생각합니다. [-2, 1]입니다. 그러나 0은 null이고 1은 메모리에서 액세스 가능한 유일한 바이트입니다.)
여전히 내 마음에 해결되지 않은 것이 있습니다. 스택 오버플로 질문 특정 고정 주소를 가리키는 포인터는 null 포인터의 0이 추상화하더라도 다른 포인터 값이 반드시 필요한 것은 아니라는 것을 알려줍니다. 이로 인해 다른 스택 오버플로 질문이 게시 됩니다. 주소 0에 액세스하고 싶습니까? .
2 점
소스 코드의 상수 값 0만이 널 포인터입니다. 컴파일러 구현은 실행 코드에서 원하는 값을 사용할 수 있습니다. 일부 플랫폼에는 구현이 널 포인터로 사용할 수있는 '잘못된'특수 포인터 값이 있습니다. C FAQ에는 "실제로 실제 기계가 0이 아닌 널 포인터를 사용하거나 다른 유형에 대한 포인터를 다르게 표현 했습니까?"라는 질문이 있습니다. 즉,이 속성 0을 C 소스의 널 포인터로 사용하고 런타임에 다르게 표시되는 여러 플랫폼을 지적합니다. C ++ 표준은 "값이 0 인 정수 상수 표현식을 변환하면 항상 널 포인터가 생성된다는 점을 분명히합니다.
음수 값은 플랫폼에서 주소처럼 사용할 수 있습니다. C 표준은 단순히 널 포인터를 나타내는 데 사용할 항목을 선택하고 0을 선택했습니다. 다른 센티넬 값을 고려했는지 확실하지 않습니다.
널 포인터에 대한 유일한 요구 사항은 다음과 같습니다.
- 실제 객체에 대한 포인터와 같지 않은 것을 비교하는 것이 보장됩니다.
- 두 개의 null 포인터는 동일하게 비교됩니다 (C ++은 이것을 동일한 유형에 대한 포인터 만 보유하면되도록 수정합니다)
역사적으로 0에서 시작하는 주소 공간은 항상 ROM이며 일부 운영 체제 또는 저수준 인터럽트 처리 루틴에 사용됩니다. 요즘에는 모든 것이 가상이기 때문에 (주소 공간 포함) 운영 체제는 모든 할당을 모든 주소에 매핑 할 수 있으므로 특히 주소 0에 아무것도 할당하지 마십시오.
"널 포인터"값인 IIRC는 0이 아닙니다. 컴파일러는 0을 시스템에 적합한 "널"값으로 변환합니다 (실제로는 항상 0이지만 반드시 그런 것은 아님). 포인터를 0과 비교할 때마다 동일한 변환이 적용됩니다. 포인터와이 특수 값 -0에 대해서만 포인터를 비교할 수 있기 때문에 프로그래머는 시스템의 메모리 표현에 대해 아는 것이 없도록 프로그래머를 격리시킵니다. 왜 그들이 42 대신에 0을 선택했는지에 관해서는, 대부분의 프로그래머가 0부터 계산을 시작하기 때문에 추측 할 것입니다.) (또한 대부분의 시스템에서 0은 첫 번째 메모리 주소이므로 편리하기를 원했습니다. 내가 실제로 묘사하는 것과 거의 같은 실제 번역은 거의 일어나지 않습니다.
포인터 컨텍스트에서 상수 0의 의미를 오해해야합니다.
C 나 C ++에서 포인터는 "값 0"을 가질 수 없습니다. 포인터는 산술 객체가 아닙니다. 캐 노드는 "제로"또는 "부정적"또는 그와 같은 숫자 값을 갖습니다. 따라서 "포인터 ... 값이 0"에 대한 귀하의 진술은 의미가 없습니다.
C & C ++에서 포인터는 예약 된 널 포인터 값을 가질 수 있습니다 . 널 포인터 값의 실제 표현은 "0"과 관련이 없습니다. 주어진 플랫폼에 절대적으로 적합한 것이 될 수 있습니다. 대부분의 플랫폼에서 널 포인터 값은 실제 0 주소 값으로 물리적으로 표시됩니다. 그러나 어떤 플랫폼에서 주소 0이 실제로 어떤 목적으로 사용되는 경우 (즉, 주소 0에서 객체를 작성해야 할 수도 있음) 해당 플랫폼의 널 포인터 값이 다를 수 있습니다. 예를 들어 물리적으로 0xFFFFFFFF
주소 값 또는 주소 값 으로 표시 될 수 있습니다 0xBAADBAAD
.
그럼에도 불구하고 주어진 플랫폼에서 null 포인터 값이 어떻게 표현되는지에 관계없이 코드에서 여전히 null 포인터를 constant로 지정 0
합니다. 주어진 포인터에 널 포인터 값을 할당하기 위해 다음과 같은 표현식을 계속 사용합니다 p = 0
. 예를 들어 원하는 것을 실현하고 적절한 널 포인터 값 표현으로 변환하는 것, 즉 주소 값을 0xFFFFFFFF
포인터에 넣는 코드로 변환하는 것은 컴파일러의 책임 p
입니다.
즉, 0
소스 코드에서 널 포인터 값을 생성한다고해서 널 포인터 값이 address와 연관되어 있다는 의미는 아닙니다 0
. 0
당신이 당신의 소스 코드에서 사용하는 것이이 널 포인터 값으로 "지적"하는 실제 물리적 주소로 전혀 관계가없는 그냥 "문법적"입니다.
그러나 메모리 주소 지정은 0에서 시작하기 때문에 0은 다른 주소만큼 유효하지 않습니까?
일부 / 많은 / 모든 운영 체제에서 메모리 주소 0은 어떤면에서 특별합니다. 예를 들어, 종종 유효하지 않거나 존재하지 않는 메모리에 매핑되므로 액세스하려고하면 예외가 발생합니다.
음수가 null이 아닌 이유는 무엇입니까?
포인터 값은 일반적으로 부호없는 숫자로 처리된다고 생각합니다. 그렇지 않으면 32 비트 포인터는 4GB 대신 2GB의 메모리 만 처리 할 수 있습니다.
내 생각에 마술 값 0은 더 적은 지시로 테스트 될 수 있기 때문에 유효하지 않은 포인터를 정의하기 위해 선택되었습니다. 일부 기계 언어는 레지스터를로드 할 때 데이터에 따라 자동으로 제로 및 부호 플래그를 설정하므로 별도의 비교 명령을 수행하지 않고 간단한로드 및 분기 명령으로 널 포인터를 테스트 할 수 있습니다.
(대부분의 ISA는로드가 아닌 ALU 명령어에만 플래그를 설정합니다. 일반적으로 C 소스를 구문 분석 할 때 컴파일러를 제외하고는 계산을 통해 포인터를 생성 하지 않습니다. 그러나 적어도 임의의 포인터 너비 상수는 필요하지 않습니다. 에 비교하십시오.)
내가 작업 한 첫 번째 컴퓨터 인 Commodore Pet, Vic20 및 C64에서 RAM은 위치 0에서 시작하여 원할 경우 null 포인터를 사용하여 읽고 쓰는 것이 완전히 유효했습니다.
컨벤션 일 뿐이라고 생각합니다. 유효하지 않은 포인터를 표시하려면 값이 있어야합니다.
주소 공간의 1 바이트 만 손실되므로 문제가되지 않습니다.
부정적인 포인터가 없습니다. 포인터는 항상 부호가 없습니다. 또한 음수가 될 수 있다면 컨벤션은 주소 공간의 절반을 잃는다는 것을 의미합니다.
C는 0을 사용하여 널 포인터를 나타내지 만 포인터 자체의 값이 0이 아닐 수도 있습니다. 그러나 대부분의 프로그래머는 널 포인터가 실제로 0 인 시스템 만 사용합니다.
하지만 왜 제로? 모든 시스템이 공유하는 주소는 하나입니다. 그리고 종종 낮은 주소는 운영 체제 목적으로 예약되므로이 값은 응용 프로그램에 대한 제한을 벗어난 값으로 작동합니다. 실수로 포인터에 정수 값을 할당하면 다른 것보다 0이 될 가능성이 높습니다.
역사적으로 응용 프로그램의 메모리 부족은 시스템 리소스가 차지했습니다. 당시에는 0이 기본 null 값이되었습니다.
이것이 현대 시스템에 반드시 해당되는 것은 아니지만, 메모리 할당이 당신에게 건네 준 것 이외의 것에 포인터 값을 설정하는 것은 여전히 나쁜 생각입니다.
나중에 "노출 오류"를 삭제하도록 포인터를 삭제 한 후 널로 포인터를 설정하지 않는 것에 대한 인수와 관련하여 ...
If you're really, really worried about this then a better approach, one that is guaranteed to work, is to leverage assert():
...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...
This requires some extra typing, and one extra check during debug builds, but it is certain to give you what you want: notice when ptr is deleted 'twice'. The alternative given in the comment discussion, not setting the pointer to null so you'll get a crash, is simply not guaranteed to be successful. Worse, unlike the above, it can cause a crash (or much worse!) on a user if one of these "bugs" gets through to the shelf. Finally, this version lets you continue to run the program to see what actually happens.
나는 이것이 묻는 질문에 대답하지 못한다는 것을 알고 있지만, 의견을 읽는 사람이 free ()로 보내질 수 있다면 포인터를 0으로 설정하지 않는 것이 '좋은 습관'으로 간주된다는 결론에 도달 할까 걱정했다. 두 번 삭제하십시오. 가능하지 않은 경우, 정의되지 않은 동작을 디버깅 도구로 사용하는 것은 결코 좋은 습관이 아닙니다. 유효하지 않은 포인터를 삭제하여 궁극적으로 발생하는 버그를 찾아낸 사람은 아무도 이것을 제안하지 않습니다. 이러한 종류의 오류는 추적하는 데 몇 시간이 걸리며 원래 문제를 다시 추적하기 어려운 완전히 예기치 않은 방식으로 프로그램에 거의 항상 영향을 미칩니다.
An important reason why many operating systems use all-bits-zero for the null pointer representation, is that this means memset(struct_with_pointers, 0, sizeof struct_with_pointers)
and similar will set all of the pointers inside struct_with_pointers
to null pointers. This is not guaranteed by the C standard, but many, many programs assume it.
In one of the old DEC machines (PDP-8, I think), the C runtime would memory protect the first page of memory so that any attempt to access memory in that block would cause an exception to be raised.
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.
(Please Read This Paragraph Before Reading the Post. I am asking anyone interested in reading this post should try to read it carefully, and of course don't downvote it until you understand it completely, thanks.)
It is now community wiki, as such if someone disagrees with any of the concepts, please modify it, with a clear and detailed explanation of what is wrong and why, and if possible please cite sources or provide proof that can be reproduced.
Answer
Here are a few other reasons that might be the underlying factors for NULL==0
- The fact that zero is false, so one can do directly
if(!my_ptr)
instead ofif(my_ptr==NULL)
. - The fact that uninitiated global integers are initialized by default to all zeros, and as such a pointer of all zeros would be considered uninitialized.
Here I Would Like to Say A Word On Other Answers
Not Because Of Syntactic Sugar
Saying that NULL is zero because of syntactic sugar, does not make too much sense, if so why not use index 0 of an array to hold it's length?
In fact C is the language that most closely resembles the internal implementation, does it make sense to say that C picked zero just because of syntactic sugar? They would rather provide a keyword null (as many other languages do) rather than mapping zero to NULL!
As such while as of today it might just syntactic sugar, it is clear that the original intention of the C language developers was not for syntactic sugar, as I will show further.
1) The Specification
Yet while it is true that the C specification speak from the constant 0 as the null pointer (section 6.3.2.3), and also define NULL to be implementation defined (section 7.19 in the C11 specification, and 7.17 in the C99 specification), the fact remains that in the book "The C Programming Language" written by the inventors of C the following is stated in section 5.4:
C guarantees that zero is never a valid address for data, so a return value of zero can be used to signal an abnormal event, in this case, no space.
Pointer and integers are not interchangeable, Zero is the sole exception: the constant zero may be assigned to a pointer, and a pointer may be compared with the constant zero. The symbolic constant NULL is often used in place of zero, as a mnemonic to indicate more clearly that this is a special value for a pointer. NULL is defined in . We will use NULL henceforth.
As one can see (from the words "zero address") at least the original intention of the authors of C were of the address zero, and not the constant zero, moreover it appears from this excerpt that the reason why the specification speaks from the constant zero is probably not to exclude an expression that evaluates to zero, but instead to include the integer constant zero to be the only integer constant allowed for use in a pointer context without casting.
2) Summary
While the specification does not say explicitly that a zero address can be treated different than the zero constant, it does not say that not, and the fact the when dealing with the null-pointer constant it does not claim it to be implementation defined as it does by the NULL defined constant, instead claim it to be zero, shows that there might be a difference between the zero constant and the zero address.
(However if this is the case I just wonder why NULL is implementation defined, since in such a case NULL can also be the constant zero, as the compiler anyway has to convert all zero constants into the actual implementation defined NULL?)
However I don not see this in real action, and in the general platforms the address zero and the constant zero are treated the same, and throw the same error message.
Furthermore the fact is that today's operating systems are actually reserving the entire first page (range 0x0000 to 0xFFFF), just to prevent access to the zero address because of C's NULL pointer, (see http://en.wikipedia.org/wiki/Zero_page, as well as "Windows Via C/C++ by Jeffrey Richter and Christophe Nasarre (published by Microsoft Press)").
Thus I would ask from anyone claiming to actually have it seen in action, to please specify the platform, and compiler, and the exact code he actually did, (although due to the vague definition in the specification [as I have shown] any compiler and platform is free to do whatever he wants).
However it apparently seems that the authors of C didn't had this in mind, and they were speaking of the "zero address", and that "C guarantees that it is never a valid address", as well as "NULL is just a mnemonic", clearly showing that it's original intention was not for "syntactic sugar".
Not Because Of The Operating System
Also claiming that the operating system denies access to address zero, for a few reasons:
1) When C was written there was no such restriction, as one can see on this wikipage http://en.wikipedia.org/wiki/Zero_page.
2) The fact is that C compilers did accessed memory address zero.
This appears to be the fact from the following paper by BellLabs (http://www.cs.bell-labs.com/who/dmr/primevalC.html)
The two compilers differ in the details in how they cope with this. In the earlier one, the start is found by naming a function; in the later, the start is simply taken to be 0. This indicates that the first compiler was written before we had a machine with memory mapping, so the origin of the program was not at location 0, whereas by the time of the second, we had a PDP-11 that did provide mapping.
(In fact as of today (as I cited references above from wikipedia and microsoft press), the reason for restricting access to the zero address is because of C's NULL pointers! So at the end it turns out to be the other way around!)
3) Remember that C is also used to write operating systems, and even C compilers!
In fact C was developed for the purpose of writing the UNIX operating system with it, and as such it appears to be no reason why they should restrict themselves from address zero.
(Hardware) Explanation On How Computers Are (Physically) Able To Access Address Zero
There is another point I want to explain here, how is it possible to reference address zero at all?
Think of it for a second, the addresses are fetched by the processor, and then sent as voltages on the memory bus, which is then used by the memory system to get to the actual address, and yet a address of zero will mean no voltage, so how is the physical hardware of the memory system accessing address zero?
The answer appears to be, that address zero is the default, and in other words address zero is always accessible by the memory system when the memory bus is completly off, and as such any request to read or write without specifying an actual address (which is the case with address zero) is automatically accessing address zero.
참고URL : https://stackoverflow.com/questions/2759845/why-is-address-zero-used-for-the-null-pointer
'Programming' 카테고리의 다른 글
POST 데이터를 URL 인코딩해야합니까? (0) | 2020.07.17 |
---|---|
list.clear () vs list = 새 ArrayList (0) | 2020.07.17 |
babel-preset-stage-0, babel-preset-stage-1 등의 차이점은 무엇입니까? (0) | 2020.07.17 |
Java EE 컨테이너의 스폰 스레드가 권장되지 않는 이유는 무엇입니까? (0) | 2020.07.17 |
여러 파일에서 Javascript의 전역 변수 (0) | 2020.07.17 |