Wertzuweisung/Initialisierung
-
@junix
Ich raube dir nur ungern deine Illusionen, aber das Prüfen eines Zeigers auf NULL vor einem Aufruf von delete ist so unnötig wie das Tragen von Pelzmänteln in der Mittagshitze.delete und delete[] haben *garantiert* keinen Effekt, wenn sie auf einen Nullpointer angewendet werden.
-
war es nicht Daniel E. der mal gesagt hat, dass es dumm ist, dass delete auf 0 Zeiger keine Auswirkung hat? wenn ja, oder wenn wer anders, wer auch immer, das gesagt hat, will ich bitte nochmal die Erklärung dazu. Kann auch sein, dass der oder diejenige, die das gesagt hat, das ironisch gemeint hat, weiß nicht mehr.
btw delete und delete [] sind doch nicht standardkonform (ANSI C) oder?
-
Original erstellt von junix:
Angenommen du hast eine KlasseUnw*****einlich. Wir sind hier in 'ANSI C'. C ist eine von C++ _fundamental_ verschiedene Programmiersprache. Sie unterscheiden sich schon bei so trivialen Dingen, wie Operatorvorrängen. Von unterschiedlichen Programmierstilen, oder Philosophien die die Sprache vorgibt mal ganz zu schweigen.
So wie man (eigentlich) vor jedem Zugriff auf einen Zeiger auf NULL testen sollte
Man überprüft nach einer Zuweisung an diesen Zeiger. Sonst nicht.
nach jedem delete ([]) NULL zuweisen sollte.
Etwas hinschreiben, was man nicht braucht und was ein Konstrukt nicht ausdrucksstärter macht, einfach als 'guten stil' zu bezeichnen (mit der atemberaubenden Begründung: »Es könnte ja sein, dass vielleicht eventuell dann irgendwo irgendetwas passiert, dass es mir hilft 'den Fehler'[1] in sekundenschnelle zu entlarven.«). Dazu wird die Lesbarkeit des Quelltextes extrem geschwächt. Das ist es nie und nimmer wert.
Original erstellt von <war>:
war es nicht Daniel E. der mal gesagt hat, dass es dumm ist, dass delete auf 0 Zeiger keine Auswirkung hat?Nicht das ich wüsste :). Es ist schade, dass sich niemand die Arbeit macht Dokumentationen zu lesen. Selbst bei free ist das schon so gewesen.
-
Hmmm Schande. Es war spät am Abend, hab gar nicht registriert dass ich hier im ANSI C Forum bin. Naja, man ersetzte new und delete durch malloc und free...
Original erstellt von HumeSikkins:
delete und delete[] haben *garantiert* keinen Effekt, wenn sie auf einen Nullpointer angewendet werden.Hmmm jo hast recht. Allerdings, initialisiert man den Zeiger ned, dann ist er auch nicht Zwingend NULL. Und kommt einer auf die Idee auf den Zeiger z.B. auf eine Struktur zuzugreifen, so dürfte die Prüfung auf NULL nach wie vor nötig sein.
Original erstellt von Daniel E.:
Man überprüft nach einer Zuweisung an diesen Zeiger. Sonst nicht.Wenn du NACH der Zuweisung irgendwas prüfen willst ist das wohl eher zu spät oder?
Naja, egal stimmt natürlich in C und C++ herrschen in diesem Punkt erhebliche unterschiede in der Philosphie.
-junix
-
ich hoff doch, du machst diese Prüfung mit assert ...
-
Original erstellt von <war>:
war es nicht Daniel E. der mal gesagt hat, dass es dumm ist, dass delete auf 0 Zeiger keine Auswirkung hat? wenn ja, oder wenn wer anders, wer auch immer, das gesagt hat, will ich bitte nochmal die Erklärung dazu. Kann auch sein, dass der oder diejenige, die das gesagt hat, das ironisch gemeint hat, weiß nicht mehr.weiss auch nicht wer das gesagt hat, aber er hatte recht.
wozu soll delete auf 0 prüfen? ich werd doch wohl besser wissen ob der zeiger zu deleten ist oder nicht.
notfalls kann ich selber auf 0 testen, aber ich sehe nicht ein warum delete ein sinnloses if machen muss, obwohl ich garantieren kann dass es kein 0-zeiger ist.PS:
ich initialisiere zeiger nie mit 0. denn das macht mein compiler für mich. und beim release modus ists weg -> keine mehrkosten.
-
Original erstellt von junix:
**M$VC6 z.B. initialisiert den Speicher konstant mit 0xCD im Debugmodus. DAS ist wirklich müllig denn man kann in keinem Moment einen Zeiger mit assert() oder ähnlichem auf Gültigkeit bzw. Initialisierung prüfen.
**das hat den vorteil das du in debuger sofort erkennst, ah da habe ich was vergessen
Original erstellt von junix:
Angenommen du hast eine Klasse, die ein char-Array verwaltet. Gemäss deiner Ansicht ist es witzlos, im Konstruktor die Variable sauber auf NULL zu initialisieren. Daraus kann man allerdings direkt einen Nutzen ziehen:besser wäre es wenn du kein standard ctor definierst oder im standard ctor trozdem etwas hineintuen, den sonst muss du in *jeder* methode prüfen oder der speicher ok ist
Original erstellt von junix:
**sofern nicht null - mittels delete [] zu löschen. (natürlich mit anschliessendem NULL zuweisen).
**das ist wohl zielmlich sinnlos in ein dtor
{ foo f; } // hier kanns du sowieso nicht mehr auf f zugreifen foo * f = new foo(); delete f; f = NULL; // das machst du doch sowieso // wozu dann also das = NULL im dtor?
wann muss man den wirklich mit NULL initialisieren?
entwerde ich initialisiere mit ein sinvollen wert bei der definition
oder ich bin sowieso 100% sicher das ich den wert zwischen der definition und der wertzuweisung nicht benutzte
aber wann habe ich den fall wo ich nicht weiß ob ich den wert benutze vor der wertzuweisung?
-
Original erstellt von junix:
Wenn du NACH der Zuweisung irgendwas prüfen willst ist das wohl eher zu spät oder?Ich sprach von Zuweisungen an den Zeiger, nicht an das Objekt, worauf er zeigen sollte ('int* p; /* Hier müsste ich prüfen? / p = malloc (42); / Jetzt prüfe ich: */p || die ("");').
-
Original erstellt von Dimah:
das hat den vorteil das du in debuger sofort erkennst, ah da habe ich was vergessenNein das kannst du nicht zweifelsfrei erkennen. Im Prinzip wäre es möglich, dass du die Adresse 0xCDCDCDCD bei angefordertem Speicher bekommst und was ist dann? Wieso nicht NULL initialisieren? NULL ist und bleibt ungültig...
Original erstellt von Dimah:
besser wäre es wenn du kein standard ctor definierst oder im standard ctor trozdem etwas hineintuen, den sonst muss du in *jeder* methode prüfen oder der speicher ok istJo, das würde dann wohl zum restlichen Design gehören wenn die Klasse die Anforderungen die ich hier skizziert habe erfüllen sollte...
Original erstellt von Dimah:
entwerde ich initialisiere mit ein sinvollen wert bei der definition
oder ich bin sowieso 100% sicher das ich den wert zwischen der definition und der wertzuweisung nicht benutzte
aber wann habe ich den fall wo ich nicht weiß ob ich den wert benutze vor der wertzuweisung?Gut zugegeben beim dtor ist es witzlos noch NULL zuzuweisen da das Objekt danach sowieso gestorben ist.
Der Fall in dem ich nicht weiss ob speicher jemals reserviert wurde trifft z.B. eben dann ein, wenn ich speicher nur reserviere wenn ich ihn auch brauche (bei der von mir als beispiel skizzierten Klasse würde nur Speicher reserviert, wenn er auch benötigt wird. ist zwar langsamer dafür weniger speicherintensiv) Hier kann ich im Idealfall 0 byte Länge im string haben und brauche also keinen Speicher (Zeiger = NULL)Original erstellt von Daniel E.:
'int p; / Hier müsste ich prüfen? /*Ne hier ghöert ein int *p = NULL; hin... da gibts nichts zu prüfen.
-junix
[ Dieser Beitrag wurde am 06.03.2003 um 12:49 Uhr von junix editiert. ]
-
Original erstellt von junix:
> int p; / Hier müsste ich prüfen? */
Ne hier ghöert ein int *p = NULL; hin...
**Die C-Sprachbeschreibung sieht das anders :).
Aber ich hab' dich schon verstanden: Es geht um so etwas wie 'Stilregeln', die hier bekanntermaßen gerne zerrupft werden.
NULL ist ein Makro (aus stdlib.h), dass zu einer Nullzeigerkonstanten evaluiert, womit p zu einem Nullzeiger wird. Ein Nullzeiger repräsentiert einen ungültigen Zeiger. Genau wie ein nicht initialisierter Zeiger auch. Das Dereferenzieren ist bei beiden laut Norm undefiniert. Einen Nullzeiger kann ich noch zusätzlich vergleichen und prüfen ob er wirklich ein Nullzeiger ist. Interessanterweise darf ich das mit einem nicht initialisierten Zeiger nicht tun (undefiniertes Verhalten).
Deiner Logik zufolge vermeidet man also Fehler, indem man dem Programmierer viel mehr Möglichkeiten zuschreibt.
Für Leute, die immer noch nicht festgestellt haben, dass man Uninitialisiertes besser nicht dereferenziert sei ein DOS empfohlen. Da macht man sich dann nämlic die Hardware kaputt und lässt dann in Zukunft die Finger von sowas.Ich halte etwas offensichtlich unnötiges im Quelltext weiterhin für Unsinn, weil es nichts mit der Aufgabe des Programmes zu tun hat.
-
Nein das kannst du nicht zweifelsfrei erkennen. Im Prinzip wäre es möglich, dass du die Adresse 0xCDCDCDCD bei angefordertem Speicher bekommst und was ist dann? Wieso nicht NULL initialisieren? NULL ist und bleibt ungültig...
weil man das mit NULL initialisieren auch vergessen kann, natürlich könnte der compilier alles NULLEN aber das ist nicht das wesen von c/c++
Der Fall in dem ich nicht weiss ob speicher jemals reserviert wurde trifft z.B. eben dann ein, wenn ich speicher nur reserviere wenn ich ihn auch brauche (bei der von mir als beispiel skizzierten Klasse würde nur Speicher reserviert, wenn er auch benötigt wird. ist zwar langsamer dafür weniger speicherintensiv) Hier kann ich im Idealfall 0 byte Länge im string haben und brauche also keinen Speicher (Zeiger = NULL)
ein NULL zeiger representiert kein 0 zeichen langen c-string
aber ich will jetzt kiene harre spalten
da ich versuche standard ctors zu vermeiden und mit sinvollen werten initialisiere baruche ich sehr selten NULL (nur wenn ich mit fremd libs & apis arbeiten muss)zweifelst du die drei regel an? standard ctor vermeiden, mit sinnvollen werten initialisieren und so spät definieren wie möglich
Für diese regelen gibt es ganz klare vorteile
wenn du sie befolgst verschwinden sowieso die meisten NULLs.
-
ich initialisiere zeiger nie mit 0. denn das macht mein compiler für mich. und beim release modus ists weg -> keine mehrkosten.
An welcher Stelle des ANSI-Standards ist eigentlich ein Unterschied zwischen Release und Debugmode definiert? (Ich will ja gern dazulernen
Ansonsten gilt die Initialisierung zu 0 durch den Compiler doch nur für globale und static Variablen.
-
nirgends, aber eine Implementation ist berechtigt, beliebige Zusatzbedingungen, die der Standard nicht definiert, zuzusichern
-
Original erstellt von Geo:
An welcher Stelle des ANSI-Standards ist eigentlich ein Unterschied zwischen Release und Debugmode definiert?In der ISO-Norm beschreibt 7.2 das assert-Makro und dessen Beziehung zu NDEBUG.
-
Original erstellt von junix:
**Nein das kannst du nicht zweifelsfrei erkennen. Im Prinzip wäre es möglich, dass du die Adresse 0xCDCDCDCD bei angefordertem Speicher bekommst und was ist dann? Wieso nicht NULL initialisieren? NULL ist und bleibt ungültig...
**bekommst du aber nicht.
dieser Wert, wird vom Compiler so gewählt das er ungültig ist. das ist der feine unterschied zwischen 0 und undefiniert.0 zeiger können sinn machen, aber sie sind unterschiedlich zu uninitialisierten zeigern.
was hast du dagegen wenn der Compiler dir diese arbeit übernimmt? das macht den code der release version schlanker und du sparst dir sinnlose default werte.
aber diese diskussion hat keinen sinn. wenn du dich nicht ändern willst dann bleib halt dabei.
aber schau mal wer aller gegenteiliger meinung ist - da solltest du mal nachdenken ob du wirklich richtig liegst...
-
Der Thread läuft ja immer noch ...
Daniels Argumente (Compilerwarnungen einschalten, Schreib-Operation vor Lesen)
sind natürlich berechtigt. Eine sauber geschriebene Software stürtzt selbstverständlich auch bei uninitialisierten Variabeln nicht ab. Mir geht
es mehr darum, dass ich beim Start einer Funktion einen definierten Zustand habe - basta. Das mag mehr Philosophie als Forschrift sein, aber ich mag einfach keine undefinierten Zustände .
In einer Klasse setze ich ja auch alle Members mittels member(0) und möchte halt in einer Funktion den selben sauberen Start (ja mir ist bewusst dass die Members in unterschiedlichen Methoden genutzt werden, nicht so die privaten Variabeln).
So brauch ich keine Laufvariabeln vor der Schleife auf null zu setzen und habe
bei Pointern, die wegen einer Exception eben NICHT gesetzt wurden, auch keine Probleme.
Ausserdem verliere ich ja bei der Null-Initialisierung rein gar nichts...Original erstellt von Shade Of Mine:
ich initialisiere zeiger nie mit 0. denn das macht mein compiler für mich. und beim release modus ists weg -> keine mehrkosten.Und was nützen dir null-initialierte Variabeln beim Enwickeln, wenn ein Fehler im Code beim Release dafür ein Crash ergibt ?
[edit]
Original erstellt von Shade Of Mine:
**aber diese diskussion hat keinen sinn. wenn du dich nicht ändern willst dann bleib halt dabei.
**Das hat wiederum was Wahres an sich
[/edit][ Dieser Beitrag wurde am 06.03.2003 um 20:55 Uhr von Solaris'dUKe editiert. ]
-
Original erstellt von Solaris'dUKe:
In einer Klasse setze ich ja auch alle Members mittels member(0) und möchte halt in einer Funktion den selben sauberen StartGenau da würde ich das auch tun. Aber in C haben solche Initialisierungen keinen tieferen Sinn. Außer meine Arbeitszeit zu vergeuden was natürlich in sofern sinnvoller ist, weil ich dann weniger von meinem Böhsen C++-Code(TM) unter die Leute bringen kann :).
-
Original erstellt von Shade Of Mine:
bekommst du aber nicht.
dieser Wert, wird vom Compiler so gewählt das er ungültig ist. das ist der feine unterschied zwischen 0 und undefiniert.Äh Shade... Erklär mir bitte plausibel wieso ich NIE die Adresse 0xCDCDCDCD als Anfangsadresse eines von mir reservierten Speicherblocks bekommen kann?
-junix
-
Original erstellt von junix:
**Äh Shade... Erklär mir bitte plausibel wieso ich NIE die Adresse 0xCDCDCDCD als Anfangsadresse eines von mir reservierten Speicherblocks bekommen kann?
**Ist halt so.
Der Compiler betreibt Magie.
keine Ahnung ob die Adresse wirklich 0xCDCDCDCD ist - es interessiert mich auch nicht.Der Compiler weist den Zeigern einfach eine Adresse zu, so dass ein dereferenzieren unweigerlich zum Absturz führt. (das ist der Sinn dieser automatischen initialisierung. welche adresse dies nun ist, ist unwichtig. darum kümmert sich der compiler)
das hat den Vorteil, dass ich nicht einen Bug durch sowas umgehen kann:
if(!p) return;Denn ich kann auf 0xCD.. nicht testen (bzw. tu es nicht)
IMHO ist es nämlich ein Fehler einen 0-Zeiger an eine Funktion zu übergeben die einen ordentlichen Zeiger verlangt. ein 0-Zeiger kann abgefangen werden, und ich werde nie merken, dass ich einen Fehler da haben (weil er sich nicht auswirkt)Bei 0xCD... wirds aber nen absturz geben (garantiert) und deshalb komm ich auf den Fehler drauf.
das bezieht sich jetzt auf den VC++ - ich nehme an, andere Compiler machen das ähnlich.
mein compiler warnt mich übrigens wenn ich eine variable verwende, bevor ich ihr einen Wert zugewiesen habe. wenn ich immer mit 0 initialisiere fällt diese Warnung logischerweise weg.
und dann kann sowas passieren:
int prozent=0; ... if(foo) prozent=getProzent(bla); int USt=Umsatz*Prozent;
und USt ist auf einmal 0 und ich weiss nicht wieso...
dann debug ich ewig bis ich drauf komme dass foo nicht immer true ist - und prozent somit manchmal keinen wert hat und somit 0 ist...da finde ich es praktisch wenn mein compiler meint:
possible use of Prozent before initialisation
(oder ähnliches)bei klassen ist das natürlich etwas anderes.
da ein Objekt einer klasse jederzeit 'komplett' (funktionsfähig) sein muss. dh ich muss im Ctor sinnvolle werte zuweisen, oder zumindest ein 'is_good' flag setzen bzw. nicht setzen (obwohl ich das für unschön halte - obwohl es manchmal nicht anders geht)
-
Original erstellt von Solaris'dUKe:
**Und was nützen dir null-initialierte Variabeln beim Enwickeln, wenn ein Fehler im Code beim Release dafür ein Crash ergibt ?
**sie werden eben nicht mit 0 initialisiert, sondern mit einem ungültigen wert. somit führt jeder zugriff auf den zeiger zu einem absturz - debugging ist erleichtert.
bzw. kann mich mein compiler warnen, dass ich den zeiger vor seiner initialisierung verwende.
Sicher, wenn ich alle 0-Zeiger abfange dann stürtzt das programm nicht ab, aber das verhalten kann anders als erwartet sein:
char* text=0; ... getWindowText(text); //diese funktion test auf 0 writeToIrgendwas(text); //test auch auf 0
ausgabe ist somit nix.
und jetzt finde so einen fehler mal...
da hab ichs doch lieber wenn ich nen absturz bekomme der mir sagt:
in getWindowText gibts nen zugriff auf nen ungültigen zeiger.dann mache ich n backtrace und sehe dass exakt hier getWindowText mit ungültigen Zeiger aufgerufen wurde.
wenn du jetzt meinst: man kann ja immer
assert(p);
schreiben:
ja, kann man.
aber wozu? mein compiler macht das für mich.OK, man kann mit pre- und postconditions arbeiten:
void getWindowText(char* buf) { precondtion(buf!=0); //... postcondition(strlen(buf)>1); }
dann würden diese 'assert' ja sinn machen.
aber in diesem fall ist ja ein ganz anderer stil gefrag (design by contract)
ausserdem gibts ja nix daran auszusetzen
assert(0); zusätzlich noch zu schreiben - warum auch nicht. ist zwar redunant, aber immer noch besser als ein
if(!p) return;
letzteres kommt leider öfters vor (und dieses p kann 0 sein verleitet ja dazu)