In C (oder C ++) sind Zeiger etwas Besonderes, wenn sie den Wert Null haben: Es wird empfohlen, Zeiger nach dem Freigeben des Speichers auf Null zu setzen, da dies bedeutet, dass das erneute Freigeben des Zeigers nicht gefährlich ist. Wenn ich malloc aufrufe, wird ein Zeiger mit dem Wert Null zurückgegeben, wenn ich keinen Speicher erhalten kann. Ich benutze die if (p != 0)
ganze Zeit, um sicherzustellen, dass übergebene Zeiger gültig sind usw.
Aber da die Speicheradressierung bei 0 beginnt, ist 0 nicht genauso eine gültige Adresse wie jede andere? Wie kann 0 für den Umgang mit Nullzeigern verwendet werden, wenn dies der Fall ist? Warum ist stattdessen keine negative Zahl null?
Bearbeiten:
Eine Menge guter Antworten. Ich werde zusammenfassen, was in den Antworten gesagt wurde, die so ausgedrückt wurden, wie mein eigener Verstand es interpretiert, und hoffe, dass die Community mich korrigiert, wenn ich es falsch verstehe.
Wie alles andere in der Programmierung ist es eine Abstraktion. Nur eine Konstante, die nicht wirklich mit der Adresse 0 zusammenhängt. C ++ 0x betont dies durch Hinzufügen des Schlüsselworts
nullptr
.Es ist nicht einmal eine Adressabstraktion, es ist die vom C-Standard festgelegte Konstante, und der Compiler kann sie in eine andere Zahl übersetzen, solange er sicherstellt, dass sie niemals einer "echten" Adresse entspricht, und anderen Nullzeigern entspricht, wenn 0 nicht die ist bester Wert für die Plattform.
Falls es sich nicht um eine Abstraktion handelt, wie dies früher der Fall war, wird die Adresse 0 vom System verwendet und ist für den Programmierer nicht zulässig.
Ich gebe zu, mein Vorschlag für eine negative Zahl war ein wenig wildes Brainstorming. Die Verwendung einer vorzeichenbehafteten Ganzzahl für Adressen ist etwas verschwenderisch, wenn der Wertraum abgesehen vom Nullzeiger (-1 oder was auch immer) gleichmäßig zwischen positiven Ganzzahlen, die gültige Adressen ergeben, und negativen Zahlen, die nur verschwendet werden, aufgeteilt wird.
Wenn eine Zahl immer durch einen Datentyp darstellbar ist, ist sie 0. (Wahrscheinlich ist 1 auch. Ich denke an die Ein-Bit-Ganzzahl, die 0 oder 1 wäre, wenn sie nicht vorzeichenbehaftet wäre, oder nur an das vorzeichenbehaftete Bit, wenn sie vorzeichenbehaftet ist, oder an die Zwei-Bit-Ganzzahl, die wäre [-2, 1]. Aber dann könnte man einfach 0 als Null und 1 als einziges zugängliches Byte im Speicher wählen.)
Trotzdem gibt es etwas, das in meinem Kopf ungelöst ist. Der Stapelüberlauf-Fragezeiger auf eine bestimmte feste Adresse sagt mir, dass andere Zeigerwerte nicht unbedingt erforderlich sind, selbst wenn 0 für Nullzeiger eine Abstraktion ist. Dies führt mich zu einer weiteren Frage zum Stapelüberlauf: Könnte ich jemals auf die Adresse Null zugreifen wollen? .
if (p != 0)
zuif (p)
einer in C und C ++ gebräuchlichen Redewendung wechseln , obwohl Sie sich aus der Gewohnheit befreien müssen, wenn Sie Java verwenden.0xDEADBEEF
.Antworten:
2 Punkte:
Nur der konstante Wert 0 im Quellcode ist der Nullzeiger. Die Compiler-Implementierung kann den gewünschten Wert im laufenden Code verwenden. Einige Plattformen haben einen speziellen Zeigerwert, der "ungültig" ist und den die Implementierung möglicherweise als Nullzeiger verwendet. In den C-FAQs wird die Frage gestellt: "Im Ernst, haben tatsächlich Maschinen wirklich Nullzeiger ungleich Null oder unterschiedliche Darstellungen für Zeiger auf verschiedene Typen verwendet?" Dies weist auf mehrere Plattformen hin, die diese Eigenschaft von 0 als Nullzeiger in der C-Quelle verwendet haben, während sie zur Laufzeit unterschiedlich dargestellt werden. Der C ++ - Standard enthält einen Hinweis, der deutlich macht, dass das Konvertieren eines integralen konstanten Ausdrucks mit dem Wert Null immer einen Nullzeiger ergibt.
Ein negativer Wert kann von der Plattform genauso verwendet werden wie eine Adresse - der C-Standard musste lediglich etwas auswählen, um einen Nullzeiger anzuzeigen, und Null wurde ausgewählt. Ich bin mir ehrlich gesagt nicht sicher, ob andere Sentinel-Werte berücksichtigt wurden.
Die einzigen Voraussetzungen für einen Nullzeiger sind:
quelle
In der Vergangenheit war der Adressraum ab 0 immer ein ROM, der für einige Betriebssystem- oder Interrupt-Behandlungsroutinen auf niedriger Ebene verwendet wurde. Da alles virtuell ist (einschließlich des Adressraums), kann das Betriebssystem jede Zuordnung einer beliebigen Adresse zuordnen, so dass dies möglich ist Weisen Sie an der Adresse 0 NICHT zu.
quelle
IIRC, der "Nullzeiger" -Wert ist nicht garantiert Null. Der Compiler übersetzt 0 in den für das System geeigneten "Null" -Wert (der in der Praxis wahrscheinlich immer Null ist, aber nicht unbedingt). Die gleiche Übersetzung wird angewendet, wenn Sie einen Zeiger mit Null vergleichen. Da Sie nur Zeiger miteinander und mit diesem Sonderwert 0 vergleichen können, wird der Programmierer davon abgehalten, etwas über die Speicherdarstellung des Systems zu wissen. Warum sie 0 anstelle von 42 oder so gewählt haben, liegt vermutlich daran, dass die meisten Programmierer bei 0 anfangen zu zählen :) (Außerdem ist auf den meisten Systemen 0 die erste Speicheradresse und sie wollten, dass sie praktisch ist, da in Übungsübersetzungen, wie ich sie beschreibe, finden selten tatsächlich statt (die Sprache erlaubt sie nur).
quelle
int* p = 0
) ein Zeiger erstellt werden, der den Wert0xdeadbeef
oder einen anderen von ihm bevorzugten Wert enthält. 0 ist ein Nullzeiger, aber ein Nullzeiger ist nicht unbedingt ein Zeiger auf die Adresse Null. :)Sie müssen die Bedeutung der Konstanten Null im Zeigerkontext falsch verstehen.
Weder in C noch in C ++ können Zeiger "den Wert Null haben". Zeiger sind keine arithmetischen Objekte. Sie können keine numerischen Werte wie "Null" oder "Negativ" oder ähnliches haben. Ihre Aussage über "Zeiger ... haben den Wert Null" macht also einfach keinen Sinn.
In C & C ++ können Zeiger den reservierten Nullzeigerwert haben . Die tatsächliche Darstellung des Nullzeigerwerts hat nichts mit "Nullen" zu tun. Es kann absolut alles sein, was für eine bestimmte Plattform geeignet ist. Es ist wahr, dass auf den meisten Plattformen der Nullzeigerwert physikalisch durch einen tatsächlichen Nulladresswert dargestellt wird. Wenn jedoch auf einer Plattform die Adresse 0 tatsächlich für einen bestimmten Zweck verwendet wird (dh Sie müssen möglicherweise Objekte unter der Adresse 0 erstellen), ist der Nullzeigerwert auf einer solchen Plattform höchstwahrscheinlich unterschiedlich. Es könnte beispielsweise physisch als
0xFFFFFFFF
Adresswert oder als0xBAADBAAD
Adresswert dargestellt werden.Unabhängig davon, wie der Nullzeigerwert auf einer bestimmten Plattform dargestellt wird, werden Sie in Ihrem Code weiterhin Nullzeiger durch Konstanten festlegen
0
. Um einem bestimmten Zeiger einen Nullzeigerwert zuzuweisen, verwenden Sie weiterhin Ausdrücke wiep = 0
. Es liegt in der Verantwortung des Compilers, zu realisieren, was Sie wollen, und es in die richtige Nullzeigerwertdarstellung zu übersetzen, dh es in den Code zu übersetzen, der den Adresswert von0xFFFFFFFF
in den Zeiger setztp
setzt.Kurz gesagt, die Tatsache, dass Sie
0
in Ihrem Sorce-Code Nullzeigerwerte generieren, bedeutet nicht, dass der Nullzeigerwert irgendwie an die Adresse gebunden ist0
. Das0
, was Sie in Ihrem Quellcode verwenden, ist nur "syntaktischer Zucker", der absolut keine Beziehung zur tatsächlichen physischen Adresse hat, auf die der Nullzeigerwert "zeigt".quelle
(p1 - nullptr) - (p2 - nullptr) == (p1 - p2)
.NULL
explizit dargestellt werden müssen , nicht durch 0.Unter einigen / vielen / allen Betriebssystemen ist die Speicheradresse 0 in irgendeiner Weise speziell. Beispielsweise wird es häufig einem ungültigen / nicht vorhandenen Speicher zugeordnet, was eine Ausnahme verursacht, wenn Sie versuchen, darauf zuzugreifen.
Ich denke, dass Zeigerwerte normalerweise als vorzeichenlose Zahlen behandelt werden: Andernfalls könnte beispielsweise ein 32-Bit-Zeiger nur 2 GB Speicher anstelle von 4 GB adressieren.
quelle
Ich würde vermuten, dass der magische Wert 0 ausgewählt wurde, um einen ungültigen Zeiger zu definieren, da er mit weniger Anweisungen getestet werden konnte. Einige Maschinensprachen setzen beim Laden von Registern automatisch die Null- und Vorzeichenflags gemäß den Daten, sodass Sie mit einem einfachen Lade- und Verzweigungsbefehl auf einen Nullzeiger testen können, ohne einen separaten Vergleichsbefehl auszuführen.
(Die meisten ISAs setzen jedoch nur Flags für ALU-Anweisungen, nicht jedoch für das Laden. Normalerweise erzeugen Sie keine Zeiger über die Berechnung, außer im Compiler, wenn Sie die C- Quelle analysieren . Zumindest benötigen Sie jedoch keine beliebige Konstante für die Zeigerbreite vergleiche mit.)
Auf dem Commodore Pet, Vic20 und C64, die die ersten Maschinen waren, an denen ich gearbeitet habe, wurde der Arbeitsspeicher an Position 0 gestartet, sodass das Lesen und Schreiben mit einem Nullzeiger absolut gültig war, wenn Sie dies wirklich wollten.
quelle
Ich denke, es ist nur eine Konvention. Es muss ein Wert vorhanden sein, um einen ungültigen Zeiger zu markieren.
Sie verlieren nur ein Byte Adressraum, das sollte selten ein Problem sein.
Es gibt keine negativen Zeiger. Zeiger sind immer ohne Vorzeichen. Auch wenn sie negativ sein könnten, würde Ihre Konvention bedeuten, dass Sie die Hälfte des Adressraums verlieren.
quelle
char *p = (char *)1; --p;
. Da das Verhalten eines Nullzeigers vom Standard nicht definiert ist, kann dieses Systemp
tatsächlich die Adresse 0 lesen und schreiben, die Adresse erhöhen1
usw.char x = ((char*)0);
. Ein solcher Code würde bei jeder Implementierung, die sein Verhalten nicht definiert hat, zu undefiniertem Verhalten führen, aber die Tatsache, dass ein Standard sagt, dass etwas undefiniertes Verhalten ist, verbietet Implementierungen in keiner Weise, ihre eigenen Spezifikationen für das, was er tun wird, anzubieten.*(char *)0
. Das stimmt, aber in meinem Vorschlag muss die Implementierung nicht das Verhalten*(char *)0
oder anderer Nullzeigeroperationen definieren .char *p = (char*)1; --p;
würde nur dann durch den Standard definiert, wenn diese Sequenz ausgeführt worden wäre, nachdem ein Zeiger auf etwas anderes als das erste Byte eines Objekts auf a umgewandelt worden wäreintptr_t
und das Ergebnis dieser Umwandlung zufällig den Wert 1 ergab und in diesem speziellen Fall würde das Ergebnis von--p
einen Zeiger auf das Byte ergeben, das dem Byte vorausgeht, dessen Zeigerwert bei Umwandlung inintptr_t
ergeben hätte1
.Obwohl C 0 verwendet, um den Nullzeiger darzustellen, beachten Sie, dass der Wert des Zeigers selbst möglicherweise keine Null ist. Die meisten Programmierer verwenden jedoch immer nur Systeme, bei denen der Nullzeiger tatsächlich 0 ist.
Aber warum Null? Nun, es ist eine Adresse, die jedes System teilt. Und oft sind die niedrigen Adressen für Betriebssystemzwecke reserviert, so dass der Wert gut funktioniert und für Anwendungsprogramme nicht zulässig ist. Die versehentliche Zuweisung eines ganzzahligen Werts zu einem Zeiger führt genauso wahrscheinlich zu Null wie alles andere.
quelle
In der Vergangenheit war der geringe Speicher einer Anwendung mit Systemressourcen belegt. In jenen Tagen wurde Null zum Standardwert für Null.
Dies gilt zwar nicht unbedingt für moderne Systeme, es ist jedoch immer noch eine schlechte Idee, Zeigerwerte auf etwas anderes als die Speicherzuweisung festzulegen, die Sie erhalten haben.
quelle
In Bezug auf das Argument, einen Zeiger nach dem Löschen nicht auf null zu setzen, damit zukünftige Löschvorgänge "Fehler anzeigen" ...
Wenn Sie sich darüber wirklich große Sorgen machen, besteht ein besserer Ansatz, der garantiert funktioniert, darin, assert () zu nutzen:
Dies erfordert einige zusätzliche Eingaben und eine zusätzliche Überprüfung während der Debug-Erstellung, aber es ist sicher, dass Sie das bekommen, was Sie wollen: Beachten Sie, wenn ptr 'zweimal' gelöscht wird. Die in der Kommentardiskussion angegebene Alternative, den Zeiger nicht auf Null zu setzen, damit Sie abstürzen, ist einfach nicht garantiert erfolgreich. Schlimmer noch, im Gegensatz zu den oben genannten, kann es zu einem Absturz (oder viel schlimmer!) Bei einem Benutzer kommen, wenn einer dieser "Fehler" in das Regal gelangt. Schließlich können Sie mit dieser Version das Programm weiter ausführen, um zu sehen, was tatsächlich passiert.
Mir ist klar, dass dies die gestellte Frage nicht beantwortet, aber ich war besorgt, dass jemand, der die Kommentare liest, zu dem Schluss kommen könnte, dass es als "gute Praxis" angesehen wird, Zeiger NICHT auf 0 zu setzen, wenn es möglich ist, dass sie an free () oder gesendet werden zweimal löschen. In diesen wenigen Fällen, in denen es möglich ist, ist es NIE eine gute Praxis, undefiniertes Verhalten als Debugging-Tool zu verwenden. Niemand, der jemals einen Fehler suchen musste, der letztendlich durch das Löschen eines ungültigen Zeigers verursacht wurde, würde dies vorschlagen. Diese Art von Fehlern dauert Stunden, um sie aufzuspüren, und wirkt sich fast immer auf eine völlig unerwartete Weise auf das Programm aus, die schwer bis unmöglich auf das ursprüngliche Problem zurückzuführen ist.
quelle
Ein wichtiger Grund, warum viele Betriebssysteme All-Bits-Null für die Nullzeigerdarstellung verwenden, ist, dass dies bedeutet, dass
memset(struct_with_pointers, 0, sizeof struct_with_pointers)
alle darin enthaltenen Zeigerstruct_with_pointers
auf Nullzeiger gesetzt werden. Dies wird vom C-Standard nicht garantiert, aber viele, viele Programme gehen davon aus.quelle
In einer der alten DEC-Maschinen (PDP-8, glaube ich) würde die C-Laufzeit die erste Speicherseite durch den Speicher schützen, sodass bei jedem Versuch, auf den Speicher in diesem Block zuzugreifen, eine Ausnahme ausgelöst wird.
quelle
Die Wahl des Sentinel-Werts ist willkürlich, und dies wird in der nächsten Version von C ++ (informell bekannt als "C ++ 0x", wahrscheinlich in Zukunft als ISO C ++ 2011 bekannt) mit der Einführung des Schlüsselwort
nullptr
zur Darstellung eines Nullwertzeigers. In C ++ kann ein Wert von 0 als Initialisierungsausdruck für jeden POD und für jedes Objekt mit einem Standardkonstruktor verwendet werden. Dies hat die besondere Bedeutung, dass bei einer Zeigerinitialisierung der Sentinel-Wert zugewiesen wird. Warum kein negativer Wert gewählt wurde, liegt normalerweise zwischen 0 und 2 N.-1 für einen Wert N. Mit anderen Worten, Adressen werden normalerweise als vorzeichenlose Werte behandelt. Wenn der Maximalwert als Sentinel-Wert verwendet würde, müsste er je nach Speichergröße von System zu System variieren, während 0 immer eine darstellbare Adresse ist. Es wird auch aus historischen Gründen verwendet, da die Speicheradresse 0 in Programmen normalerweise unbrauchbar war und heutzutage in den meisten Betriebssystemen Teile des Kernels in die unteren Speicherseiten geladen sind und solche Seiten normalerweise so geschützt sind, dass wenn Berühren (Dereferenzieren) durch ein Programm (Speichern des Kernels) führt zu einem Fehler.quelle
Es muss einen Wert haben. Offensichtlich möchten Sie nicht auf Werte treten, die der Benutzer möglicherweise rechtmäßig verwenden möchte. Ich würde spekulieren, dass es einen gewissen Sinn macht, Null als nicht initialisierten Zeigerwert zu interpretieren, da die C-Laufzeit das BSS-Segment für nullinitialisierte Daten bereitstellt.
quelle
In seltenen Fällen können Sie mit einem Betriebssystem an Adresse 0 schreiben. nämlich IDTs, Seitentabellen usw. (Die Tabellen müssen sich im RAM befinden, und es ist einfacher, sie unten zu platzieren, als zu versuchen, festzustellen, wo sich die Oberseite des RAM befindet.) Und kein Betriebssystem, das bei Verstand ist, lässt Sie zu Bearbeiten Sie Systemtabellen wohl oder übel.
Dies war K & R vielleicht nicht in den Sinn gekommen, als sie C gemacht haben, aber es (zusammen mit der Tatsache, dass 0 == null ziemlich leicht zu merken ist) macht 0 zu einer beliebten Wahl.
quelle
Der Wert
0
ist ein spezieller Wert, der in bestimmten Ausdrücken verschiedene Bedeutungen annimmt. Im Fall von Zeigern wird es, wie schon oft erwähnt, wahrscheinlich verwendet, weil es zu der Zeit die bequemste Art war, "hier den Standard-Sentinel-Wert einfügen" zu sagen. Als konstanter Ausdruck hat er im Kontext eines Zeigerausdrucks nicht die gleiche Bedeutung wie bitweise Null (dh alle auf Null gesetzten Bits). In C ++ gibt es mehrere Typen, die keine bitweise Nulldarstellung haben,NULL
z. B. Zeigerelement und Zeiger auf Elementfunktion.Zum Glück hat C ++ 0x ein neues Schlüsselwort für "Ausdruck, der einen bekannten ungültigen Zeiger bedeutet, der für integrale Ausdrücke nicht auch bitweise Null zugeordnet ist" :
nullptr
. Obwohl es einige Systeme gibt, auf die Sie mit C ++ abzielen können und die eine Dereferenzierung der Adresse 0 ohne Barfing ermöglichen, sollten Sie sich vor Programmierern in Acht nehmen.quelle
In diesem Thread gibt es bereits viele gute Antworten. Es gibt wahrscheinlich viele verschiedene Gründe, den Wert
0
für Nullzeiger zu bevorzugen , aber ich werde zwei weitere hinzufügen:quelle
Dies hängt von der Implementierung von Zeigern in C / C ++ ab. Es gibt keinen bestimmten Grund, warum NULL bei Zuweisungen zu einem Zeiger gleichwertig ist.
quelle
Dafür gibt es historische Gründe, aber es gibt auch Optimierungsgründe dafür.
Es ist üblich, dass das Betriebssystem einen Prozess mit auf 0 initialisierten Speicherseiten bereitstellt. Wenn ein Programm einen Teil dieser Speicherseite als Zeiger interpretieren möchte, ist es 0, sodass das Programm leicht feststellen kann, dass dieser Zeiger ist nicht initialisiert. (Dies funktioniert nicht so gut, wenn es auf nicht initialisierte Flash-Seiten angewendet wird.)
Ein weiterer Grund ist, dass es auf vielen Prozessoren sehr einfach ist, die Äquivalenz eines Werts mit 0 zu testen. Manchmal handelt es sich um einen kostenlosen Vergleich, bei dem keine zusätzlichen Anweisungen erforderlich sind, und der normalerweise durchgeführt werden kann, ohne dass in einem anderen Register oder ein Wert von Null angegeben werden muss als Literal im Anweisungsstrom zu vergleichen.
Die billigen Vergleiche für die meisten Prozessoren sind die Vorzeichen kleiner als 0 und gleich 0. (Vorzeichen größer als 0 und ungleich 0 werden von beiden impliziert)
Da 1 Wert von allen möglichen Werten als schlecht oder nicht initialisiert reserviert werden muss, können Sie ihn genauso gut zu dem Wert machen, der den billigsten Test für die Gleichwertigkeit mit dem schlechten Wert hat. Dies gilt auch für mit '\ 0' abgeschlossene Zeichenfolgen.
Wenn Sie versuchen würden, für diesen Zweck mehr oder weniger als 0 zu verwenden, würden Sie Ihren Adressbereich in zwei Hälften teilen.
quelle
Die Konstante
0
verwendet wird , statt ,NULL
weil C vor von einigen cavemen Billionen von Jahren gemacht wurde,NULL
,NIL
,ZIP
, oderNADDA
hätte alles gemacht viel mehr Sinn als0
.Tatsächlich. Obwohl viele Betriebssysteme es Ihnen nicht erlauben, irgendetwas an der Adresse Null zuzuordnen, selbst in einem virtuellen Adressraum (die Leute erkannten, dass C eine unsichere Sprache ist, und weil Nullzeiger-Dereferenzierungsfehler sehr häufig sind, haben Sie beschlossen, sie zu beheben, indem Sie das nicht zulassen Userspace-Code, der Seite 0 zugeordnet werden soll. Wenn Sie also einen Rückruf aufrufen, der Rückrufzeiger jedoch NULL ist, wird am Ende kein beliebiger Code ausgeführt.
Da die
0
Verwendung im Vergleich zu einem Zeiger durch einen implementierungsspezifischen Wert ersetzt wird, der der Rückgabewert von malloc bei einem Malloc-Fehler ist.Das wäre noch verwirrender.
quelle
int
nicht nur die gleiche Größe wie ein Zeiger hatte - in vielen Kontexten konnten einint
und ein Zeiger austauschbar verwendet werden. Wenn eine Routine einen Zeiger erwartete und eine in einer ganzen Zahl 57 übergeben wurde, würde die Routine eine Adresse mit demselben Bitmuster wie die Zahl 57 verwenden. Auf diesen bestimmten Maschinen war das Bitmuster zur Bezeichnung eines Nullzeigers 0, so dass eine int 0 übergeben wurde würde einen Nullzeiger übergeben.( Bitte lesen Sie diesen Absatz, bevor Sie den Beitrag lesen.Ich bitte jeden, der daran interessiert ist, diesen Beitrag zu lesen, zu versuchen, ihn sorgfältig zu lesen, und natürlich nicht abzustimmen, bis Sie ihn vollständig verstanden haben, danke. )
Es ist jetzt ein Community-Wiki. Wenn jemand mit einem der Konzepte nicht einverstanden ist, ändern Sie es bitte mit einer klaren und detaillierten Erklärung, was falsch ist und warum, und wenn möglich, zitieren Sie Quellen oder liefern Sie Beweise, die reproduziert werden können.
Antworten
Hier sind einige andere Gründe, die die zugrunde liegenden Faktoren für NULL == 0 sein könnten
if(!my_ptr)
statt tunif(my_ptr==NULL)
.Hier möchte ich ein Wort zu anderen Antworten sagen
Nicht wegen syntaktischen Zuckers
Zu sagen, dass NULL wegen syntaktischen Zuckers Null ist, macht nicht allzu viel Sinn. Wenn ja, warum nicht den Index 0 eines Arrays verwenden, um seine Länge zu halten?
Tatsächlich ist C die Sprache, die der internen Implementierung am ähnlichsten ist. Ist es sinnvoll zu sagen, dass C nur wegen syntaktischen Zuckers Null gewählt hat? Sie würden lieber ein Schlüsselwort null angeben (wie es viele andere Sprachen tun), als null auf NULL abzubilden!
Obwohl es sich heute möglicherweise nur um syntaktischen Zucker handelt, ist klar, dass die ursprüngliche Absicht der Entwickler der C-Sprache nicht syntaktischer Zucker war, wie ich weiter zeigen werde.
1) Die Spezifikation
Zwar spricht die C-Spezifikation von der Konstanten 0 als Nullzeiger (Abschnitt 6.3.2.3) und definiert auch NULL als implementierungsdefiniert (Abschnitt 7.19 in der C11-Spezifikation und 7.17 in der C99-Spezifikation) Tatsache bleibt, dass in dem von den Erfindern von C verfassten Buch "The C Programming Language" in Abschnitt 5.4 Folgendes angegeben ist:
Wie man sehen kann (aus den Worten "Nulladresse"), war zumindest die ursprüngliche Absicht der Autoren von C die Adresse Null und nicht die Konstante Null, außerdem geht aus diesem Auszug hervor, dass der Grund, warum die Spezifikation von der spricht Die Konstante Null soll wahrscheinlich keinen Ausdruck ausschließen, der als Null ausgewertet wird, sondern stattdessen die Ganzzahlkonstante Null als einzige Ganzzahlkonstante einschließen, die in einem Zeigerkontext ohne Umwandlung verwendet werden darf.
2) Zusammenfassung
Während die Spezifikation nicht explizit besagt, dass eine Nulladresse anders als die Nullkonstante behandelt werden kann, sagt sie dies nicht, und die Tatsache, dass beim Umgang mit der Nullzeigerkonstante nicht behauptet wird, dass sie als solche definiert ist Wenn die von NULL definierte Konstante stattdessen behauptet, sie sei Null, zeigt dies, dass möglicherweise ein Unterschied zwischen der Nullkonstante und der Nulladresse besteht.
(Wenn dies jedoch der Fall ist, frage ich mich nur, warum NULL implementierungsdefiniert ist, da in einem solchen Fall NULL auch die Konstante Null sein kann, da der Compiler ohnehin alle Nullkonstanten in die tatsächlich implementierte Implementierung NULL konvertieren muss.)
Ich sehe dies jedoch nicht in der realen Aktion, und auf den allgemeinen Plattformen werden die Adresse Null und die Konstante Null gleich behandelt und werfen die gleiche Fehlermeldung aus.
Tatsache ist außerdem, dass die heutigen Betriebssysteme tatsächlich die gesamte erste Seite (Bereich 0x0000 bis 0xFFFF) reservieren, um den Zugriff auf die Nulladresse aufgrund des NULL-Zeigers von C zu verhindern (siehe http://en.wikipedia.org/wiki/). Zero_page sowie "Windows Via C / C ++ von Jeffrey Richter und Christophe Nasarre (herausgegeben von Microsoft Press)").
Daher würde ich jeden, der behauptet, es tatsächlich in Aktion gesehen zu haben, bitten, die Plattform und den Compiler sowie den genauen Code anzugeben, den er tatsächlich gemacht hat (obwohl aufgrund der vagen Definition in der Spezifikation [wie ich gezeigt habe] jeder Compiler und die Plattform ist frei zu tun, was er will).
Es scheint jedoch, dass die Autoren von C dies nicht im Sinn hatten und von der "Null-Adresse" sprachen und dass "C garantiert, dass es sich nie um eine gültige Adresse handelt" sowie "NULL ist nur eine" Mnemonik ", was deutlich zeigt, dass die ursprüngliche Absicht nicht" syntaktischer Zucker "war.
Nicht wegen des Betriebssystems
Außerdem wird behauptet, dass das Betriebssystem aus mehreren Gründen den Zugriff auf die Adresse Null verweigert:
1) Als C geschrieben wurde, gab es keine solche Einschränkung, wie man auf dieser Wikipage http://en.wikipedia.org/wiki/Zero_page sehen kann .
2) Tatsache ist, dass C-Compiler auf die Speicheradresse Null zugegriffen haben.
Dies scheint die Tatsache aus dem folgenden Artikel von BellLabs ( http://www.cs.bell-labs.com/who/dmr/primevalC.html ) zu sein.
(Tatsächlich liegt der Grund für die Einschränkung des Zugriffs auf die Nulladresse ab heute (wie ich oben in Wikipedia und Microsoft Press zitiert habe) in den NULL-Zeigern von C! Am Ende stellt sich heraus, dass es umgekehrt ist!)
3) Denken Sie daran, dass C auch zum Schreiben von Betriebssystemen und sogar von C-Compilern verwendet wird!
Tatsächlich wurde C entwickelt, um das UNIX-Betriebssystem damit zu schreiben, und als solches scheint es kein Grund zu sein, sich von der Adresse Null zu beschränken.
(Hardware) Erläuterung, wie Computer (physisch) auf die Adresse Null zugreifen können
Es gibt noch einen weiteren Punkt, den ich hier erläutern möchte: Wie ist es überhaupt möglich, auf die Adresse Null zu verweisen?
Denken Sie eine Sekunde darüber nach, die Adressen werden vom Prozessor abgerufen und dann als Spannungen auf dem Speicherbus gesendet, der dann vom Speichersystem verwendet wird, um zur tatsächlichen Adresse zu gelangen, und dennoch bedeutet eine Adresse von Null keine Spannung Wie greift die physische Hardware des Speichersystems auf die Adresse Null zu?
Die Antwort scheint zu sein, dass die Adresse Null die Standardeinstellung ist, und mit anderen Worten, die Adresse Null ist für das Speichersystem immer zugänglich, wenn der Speicherbus vollständig ausgeschaltet ist, und als solche jede Anforderung zum Lesen oder Schreiben, ohne eine tatsächliche Adresse anzugeben (welche ist der Fall bei Adresse Null) greift automatisch auf Adresse Null zu.
quelle