Kann jemand bitte erklären, warum Zeiger nicht initialisiert sind NULL
?
Beispiel:
void test(){
char *buf;
if (!buf)
// whatever
}
Das Programm würde nicht in das if weil treten buf
eintreten, es nicht null ist.
Ich würde gerne wissen, warum. In welchem Fall benötigen wir eine Variable mit aktiviertem Papierkorb, insbesondere Zeiger, die den Papierkorb im Speicher adressieren?
c++
memory
pointers
initialization
Jonathan
quelle
quelle
Antworten:
Wir alle wissen, dass Zeiger (und andere POD-Typen) initialisiert werden sollten.
Die Frage lautet dann: Wer sollte sie initialisieren?
Grundsätzlich gibt es zwei Methoden:
Nehmen wir an, der Compiler hat alle Variablen initialisiert, die vom Entwickler nicht explizit initialisiert wurden. Dann stoßen wir auf Situationen, in denen die Initialisierung der Variablen nicht trivial war und der Entwickler dies am Deklarationspunkt nicht getan hat, weil er eine Operation ausführen und dann zuweisen musste.
Jetzt haben wir die Situation, dass der Compiler dem Code, der die Variable auf NULL initialisiert, eine zusätzliche Anweisung hinzugefügt hat. Später wird der Entwicklercode hinzugefügt, um die korrekte Initialisierung durchzuführen. Oder unter anderen Bedingungen wird die Variable möglicherweise nie verwendet. Viele C ++ - Entwickler würden unter beiden Bedingungen auf Kosten dieser zusätzlichen Anweisung schlecht schreien.
Es ist nicht nur an der Zeit. Aber auch Platz. Es gibt viele Umgebungen, in denen beide Ressourcen knapp sind und die Entwickler auch nicht aufgeben möchten.
ABER : Sie können den Effekt des Erzwingens der Initialisierung simulieren. Die meisten Compiler warnen Sie vor nicht initialisierten Variablen. Deshalb drehe ich meine Warnstufe immer auf die höchstmögliche Stufe. Weisen Sie dann den Compiler an, alle Warnungen als Fehler zu behandeln. Unter diesen Bedingungen generieren die meisten Compiler dann einen Fehler für Variablen, die nicht initialisiert, aber verwendet werden, und verhindern somit die Generierung von Code.
quelle
Zitieren von Bjarne Stroustrup in TC ++ PL (Special Edition S.22):
quelle
D
macht. Wenn Sie keine Initialisierung wünschen, verwenden Sie diese Syntaxfloat f = void;
oderint* ptr = void;
. Jetzt wird es standardmäßig initialisiert, aber wenn Sie es wirklich brauchen, können Sie den Compiler daran hindern.Weil die Initialisierung Zeit braucht. In C ++ sollten Sie als erstes mit einer Variablen explizit initialisieren:
oder:
oder:
quelle
Denn eines der Mottos von C ++ lautet:
Sie zahlen nicht für das, was Sie nicht verwenden
Aus diesem Grund prüft
operator[]
dievector
Klasse beispielsweise nicht, ob der Index außerhalb der Grenzen liegt.quelle
Aus historischen Gründen, hauptsächlich weil dies in C so gemacht wird, warum es in C so gemacht wird, ist eine andere Frage, aber ich denke, dass das Null-Overhead-Prinzip irgendwie in diese Entwurfsentscheidung involviert war.
quelle
Außerdem haben wir eine Warnung, wenn Sie es blasen: "wird möglicherweise verwendet, bevor ein Wert zugewiesen wird" oder eine ähnliche Aussprache, abhängig von Ihrem Compiler.
Sie kompilieren mit Warnungen, richtig?
quelle
Es gibt nur wenige Situationen, in denen es jemals Sinn macht, eine Variable nicht zu initialisieren, und die Standardinitialisierung ist mit geringen Kosten verbunden. Warum also?
C ++ ist nicht C89. Zur Hölle, sogar C ist nicht C89. Sie können Deklarationen und Code mischen, daher sollten Sie die Deklaration so lange verschieben, bis Sie einen geeigneten Wert zum Initialisieren haben.
quelle
Ein Zeiger ist nur ein anderer Typ. Wenn Sie einen oder einen anderen POD-Typ erstellen
int
,char
wird dieser nicht auf Null initialisiert. Warum sollte ein Zeiger also? Dies kann für jemanden, der ein solches Programm schreibt, als unnötiger Aufwand angesehen werden.Wenn Sie wissen, dass Sie es initialisieren werden, warum sollte das Programm Kosten verursachen, wenn Sie es zum ersten Mal
pBuf
oben in der Methode erstellen ? Dies ist das Null-Overhead-Prinzip.quelle
Wenn Sie einen Zeiger möchten, der immer auf NULL initialisiert ist, können Sie eine C ++ - Vorlage verwenden, um diese Funktionalität zu emulieren:
quelle
Foo *a
, verwenden SieInitializedPointer<Foo> a
- Eine rein akademische Übung, daFoo *a=0
weniger tippen. Der obige Code ist jedoch aus pädagogischer Sicht sehr nützlich. Mit einer kleinen Modifikation (des "Platzhalters" ctor / dtor und der Zuweisungsoperationen) könnte es leicht auf verschiedene Arten von intelligenten Zeigern erweitert werden, einschließlich Zeigern mit Gültigkeitsbereich (die auf dem Destruktor frei sind) und Zeigern mit Referenzzählung durch Hinzufügen von inc / dec-Operationen, wenn der m_pPointer gesetzt oder gelöscht ist.Beachten Sie, dass statische Daten auf 0 initialisiert werden (sofern Sie nichts anderes angeben).
Und ja, Sie sollten Ihre Variablen immer so spät wie möglich und mit einem Anfangswert deklarieren. Code wie
sollte Alarmglocken auslösen, wenn Sie es lesen. Ich weiß nicht, ob Flusen dazu überredet werden können, darüber zu karpfen, da dies zu 100% legal ist.
quelle
Ein weiterer möglicher Grund dafür ist, dass Zeiger zur Verbindungszeit eine Adresse erhalten, die indirekte Adressierung / De-Referenzierung eines Zeigers jedoch in der Verantwortung des Programmierers liegt. Normalerweise kümmert es den Compiler nicht weniger, aber die Last wird an den Programmierer weitergegeben, um die Zeiger zu verwalten und sicherzustellen, dass keine Speicherlecks auftreten.
Kurz gesagt, sie werden in dem Sinne initialisiert, dass die Zeigervariable zur Verbindungszeit eine Adresse erhält. In Ihrem obigen Beispielcode wird garantiert, dass dies abstürzt oder ein SIGSEGV generiert.
Initialisieren Sie aus Gründen der Vernunft immer Zeiger auf NULL, wenn Sie versuchen, sie ohne
malloc
oder zu dereferenzierennew
Willen clue den Programmierer in den Grund , warum das Programm falsch verhalten hat .Hoffe das hilft und macht Sinn,
quelle
Nun, wenn C ++ Zeiger initialisieren würde, dann hätten die C-Leute, die sich beschweren "C ++ ist langsamer als C", etwas Reales, an dem sie sich festhalten könnten;)
quelle
C ++ hat einen C-Hintergrund - und es gibt einige Gründe dafür:
C, sogar mehr als C ++ ist ein Assembler-Ersatz. Es tut nichts, was Sie ihm nicht sagen. Dafür: Wenn du es NULL machen willst - mach es!
Wenn Sie Dinge in einer Bare-Metal-Sprache wie C auf Null setzen, tauchen automatisch Konsistenzfragen auf: Wenn Sie etwas mallocieren - sollte es automatisch auf Null gesetzt werden? Was ist mit einer Struktur, die auf dem Stapel erstellt wurde? sollten alle Bytes auf Null gesetzt werden? Was ist mit globalen Variablen? was ist mit einer Aussage wie "(* 0x18);" Bedeutet das dann nicht, dass die Speicherposition 0x18 auf Null gesetzt werden sollte?
quelle
calloc()
.Was sind diese Hinweise, von denen Sie sprechen?
Für Ausnahme Sicherheit, immer verwenden
auto_ptr
,shared_ptr
,weak_ptr
und ihre andere Varianten.Ein Kennzeichen für guten Code ist ein Code, der keinen einzigen Aufruf an enthält
delete
.quelle
auto_ptr
und ersetzenunique_ptr
.Oh Junge. Die eigentliche Antwort ist, dass es einfach ist, den Speicher auf Null zu setzen, was eine grundlegende Initialisierung für beispielsweise einen Zeiger darstellt. Das hat auch nichts mit der Initialisierung des Objekts selbst zu tun.
Angesichts der Warnungen, die die meisten Compiler auf höchster Ebene geben, kann ich mir nicht vorstellen, auf höchster Ebene zu programmieren und sie als Fehler zu behandeln. Da mir das Aufdrehen noch nie einen Fehler in großen Mengen an produziertem Code erspart hat, kann ich dies nicht empfehlen.
quelle
NULL
Initialisieren genauso ein Fehler.