Wertzuweisung/Initialisierung



  • Worin besteht der Unterschied bei Variablen zwischen Wertezuweisung

    int a;
    a = 1;

    und Initialisierung

    int a = 1;



  • Eine Zuweisung geht (wenn die Variable passend vereinbart ist) immer, eine Initialisierung nur bei der Definition?

    Bei normalen, eingebauten Typen, die man nicht const haben will spielt das wohl keine ernstzunehmende Rolle (außer man postuliert die Existenz von Compilern, die Code erzeugen, der zB nicht initialisierte Variablen initialisiert und dann anschließend eine Zuweisung vornimmt ...), wohl aber hier:

    /* 1 */
    struct foo {
      int a;
      char b;
      unsigned c;
    };
    
    struct foo bar = { 0xa, 'b', 0xc };
    /* vs */
    struct foo bar; bar.a = 0xa; bar.b = 'b'; bar.c = 0xc;
    
    /* 2 */
    int const a = 42;
    a = 43; /* Fehler */
    

    In C versucht man, der Übersicht wegen normalerweise alle Variablen uninitialisiert an den Block- oder Funktionsanfang zu schreiben und sie dann 'nachträglich' direkt mit sinnvollen Werten zu belegen. Andere denken, dass sei extrem fehleranfällig und initialisieren alle Werte mit so allgemein anerkannten sinnlosen Konstanten wie 0.



  • Wesshalb soll es sinnlos sein, einen Pointer mit Null zu initialisieren ??
    Ich halte uninitialisierte Variablen nicht für "extrem gefährlich", aber nach
    meiner Erfahrung genügt ein einziger solcher Fehler in 50'000 Zeilen vollkommen um einen Programmierer ein paar Tage zu beschäftigen..



  • Original erstellt von Solaris'dUKe:
    Wesshalb soll es sinnlos sein, einen Pointer mit Null zu initialisieren ??

    weil der compiler im debug modus den zeiger sowieso ungültig initialisiert...



  • Original erstellt von Shade Of Mine:
    weil der compiler im debug modus den zeiger sowieso ungültig initialisiert...

    Compiler-, bzw. Debuggerabhängig... und zum Glück machen wir ja nie nen Release-Compile, nichtwahr?

    Ausserdem hat Solaris'dUKe recht. Man sollte Werte immer auf einen definierten Anfangszustand bringen. Alles andere ist schlampig.

    -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.

    -junix



  • Original erstellt von Shade Of Mine:
    weil der compiler im debug modus den zeiger sowieso ungültig initialisiert...

    Also beim Sun-Compiler steht in "char *sonstwas;" was zufälliges... Debug-Modus hin oder her...

    [ Dieser Beitrag wurde am 05.03.2003 um 16:55 Uhr von Solaris'dUKe editiert. ]



  • Original erstellt von Solaris'dUKe:
    **Wesshalb soll es sinnlos sein, einen Pointer mit Null zu initialisieren ??
    **

    Es lenkt vom eigentlichen Programm ab.
    Es wird ja gerade noch möglich sein, seine 15-zeilige Funktion zu durchschauen. Wenn man dann immer noch Lese- vor Schreiboperation durchführt, hat man ganz andere Probleme. An anderen Stellen ('global', statisch) erledigt der Compiler das.

    Warum ist es sinnvoll, einen Zeiger mit NULL zu initialisieren? Unter DOS war das zB klar definiert und hat nicht zum Perogrammabsturz geführt.

    Ich halte uninitialisierte Variablen nicht für "extrem gefährlich", aber nach meiner Erfahrung genügt ein einziger solcher Fehler in 50'000 Zeilen vollkommen um einen Programmierer ein paar Tage zu beschäftigen..

    Compilerwarnungen einsachalten. 'Von dieser Variable wird nur gelesen'.

    [Das Dereferenzieren von nicht initaialisierten Zeigern gewöhnt man sich unter DOS schnell ab, wenn man die erste Hardware kaputt gemacht hat.]

    Man sollte Werte immer auf einen definierten Anfangszustand bringen.

    Einfach, damit man was definiert hat? Was soll das nützten?

    Initialisierung sagt mir (dem Leser): Der Autor ist der Überzeugung, dass diese Variable mit diesem Wert etwas sinnvolles tut. Was genau sinnvoll ist, legt die C-Norm leider nicht fest. Nun hält nun A 0 für den Defaultwert schlechthin. B hält 42 für geeigneter. Was genau daran sinnvoll sein soll, außer, dass ich mich fragen muss, was der Autor sich dabei gedacht hat, ist mir nicht klar.



  • Original erstellt von Daniel E.:
    Einfach, damit man was definiert hat? Was soll das nützten?

    Du solltest alles lesen. Du kannst zum Beispiel zuverlässig den Zeiger auf NULL prüfen vor dem Löschen z.B.
    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:

    Initialisiere ich den Zeiger mit null, so kann ich mich im Destruktor darauf beschränken, den Zeiger auf NULL zu testen und - sofern nicht null - mittels delete [] zu löschen. (natürlich mit anschliessendem NULL zuweisen).

    Angenommen (fiktivies Beispiel) es wird innerhalb eines BLocks eine Instanz der Klasse auf dem Stack erstellt welche nichtmehr benutzt wird (im Zuge von änderungen wurde sie vielleicht vergessen oder so). Bei deiner Methode würde die Prüfung auf NULL nutzlos werden und das Programm würde versuchen einen Zeiger zu löschen auf ein char array das gar ned existiert...

    nur um mal so ein Beispiel zu nennen. Es ist schlicht einfach guter stil. So wie man (eigentlich) vor jedem Zugriff auf einen Zeiger auf NULL testen sollte (vor Allem vor löschungen etc.) oder nach jedem delete ([]) NULL zuweisen sollte.

    -junix



  • @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 Klasse

    Unw*****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 vergessen

    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...

    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 ist

    Jo, 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.


Anmelden zum Antworten