C#, Java und Zeiger



  • Ich verstehe nicht warum Java und C# Programmierer so fest daran glauben, dass Zeiger etwas böses sind, wenn doch jedes Objekt in Java/C# ja ein Zeiger ist.

    Statt einem Access Violation knallt mir eine NullReferenceException um die Ohren.

    In wiefern unterscheiden sich Objekte in Java/C# von Zeigern auf Objekte in C++?
    In wiefern ist der Java/C# Ansatz (bis auf den GC) besser?

    Das hat mich schon immer interessiert - vorallem da ich in letzter Zeit sehr viel mit C# zu tun hatte frage ich mich immer mehr was daran so genial sein soll...

    Worin liegt der Vorteil Objekte in einem undefinierten Zustand haben zu können?
    Da man keine Destruktoren hat, muss ich zB bei meiner Datenbankverbindung händisch Close() aufrufen. dh, dass ich das Objekte schließe, bevor es den Scope verlässt (mal abgesehen davon, dass man unzählige try, catch, finnalys braucht) muss ich dauernd aufpassen, dass alle Objekte in einem gültigen Zustand sind.

    Ich will hier keinen Flamewar (auch wenn sichs wohl nicht verhindern lässt :() Sondern einfach eine Erklärung, warum der Java/C# Ansatz 'besser' ist.

    PS: bevor jetzt alle Java/C#'ler schreien: Wenn ich Java/C# nicht mögen würde, würde ich wohl kaum darin programmieren, oder?



  • Shade Of Mine schrieb:

    Ich verstehe nicht warum Java und C# Programmierer so fest daran glauben, dass Zeiger etwas böses sind, wenn doch jedes Objekt in Java/C# ja ein Zeiger ist.

    Nicht Zeiger sind "böse", sondern Zeigerarithmetik inklusive der Möglichkeit, einen Zeiger auf eine beliebige Speicherstelle zeigen zu lassen. Damit läßt sich das wohl gut zusammenfassen. Mit dem GC hat das nur sehr wenig zu tun, ausser dass das Zeigermodell von C++ GC extrem erschwert.



  • Na, alles was neu ist, ist doch modern, und damit gut ...

    jeder programmer hatte in seiner newbiezeit zumindest einmal aerger mit Zeigern, also memory leaks ... und null pointer exceptions. Das ist doch der ansatzpunkt fuer Firmen wie <keine namen hier> diesen Misstand zu beheben, neue standards zu erheben und andere standards zu untergraben, und nicht zuletzt, viel geld damit zu machen. Ist doch alles nur zu unserem besten. Und auch die 25ste version von GC's spielt noch ne menge geld ein, wenn sie in ner anderen Sprache verpackt ist, mit der 25sten version von Datenbank-Objekten.

    Ist doch wie im real life. Schnelle autos machen mehr unfaelle. stimmt zwar ned ganz, klingt aber erst mal schluessig. wie lange muessen wir noch warten, bis es bald nen system gibt, wo auf der autobahn die geschwindigkeit nimmer selber regeln kannst, sondern die dir von nem leitsystem und der car electronic vorgegeben wird. Klar wird es dadurch sicherer ... aber wie lange darf man dann noch selbst bestimmen, wo man hinwill :p

    Noch 10 oder 20 jahre, und die Entwicklungssystem sind so ausgereift, das deine Klassen nur noch grafisch erstelllst, also keinen einzigen buchstaben als code mehr tippen musst, nur noch mouse clicks. Und du regst dich ueber zeiger auf ? sei froh dass den + operator noch per hand eintippern darfst !!! 😃 😃 😃

    (ich hoffe dann gibts auch noch opensource als alternative)

    Aber im ernst ... es gibt doch Gottseidank noch die Alternative. Du musst ja nich ... Du musst nur besser egumentieren koennen, als dein projektmanager.
    Ich bin auch kein Fan fuer VB. aber fuer die meisten Tools, wo der kunde eh kaum gled fuer ausgeben will, die auch ned im grossen stil laufen ... langts doch allemal... und sag mir ned, das es mit VB z.b ned schneller geht, als das selbe mit VC++ . 😃

    Ciao ....



  • Bashar schrieb:

    Nicht Zeiger sind "böse", sondern Zeigerarithmetik inklusive der Möglichkeit, einen Zeiger auf eine beliebige Speicherstelle zeigen zu lassen.

    Und wie werden dann 'NullReferenceExceptions' gerechtfertigt?
    Da zeigt der Zeiger ja auch auf eine ungueltige Speicherstelle



  • Die richtig übel zu debuggenden Probleme sind nicht die, wo der Zeiger=NULL ist, sondern die wo er *irgendwohin* zeigt. (eben nach übler Zeigerarithmetik)
    Das ist doch das, was mit dem Ansatz von C# und Java ausgeschlossen wird, oder?
    Dort gibt's nur gültige Zeiger oder eben NULL-Zeiger. Aber letztere hat man im Griff.



  • Das wesentliche an Zeigern ist, dass sie optional auch auf *nichts* zeigen können. Ohne das wär das ganze ziemlich sinnlos. Wie willst du denn zb eine verkettete Liste terminieren, oder einen Baum ...
    Das bringt es mit sich, dass es einen bestimmten Zeigerwert geben muss, der dieses nichts symbolisiert. Man könnte das einerseits so machen:

    Object __nothing;
    void * null = &__nothing;
    

    Man müsste bei jeder Dereferenzierung des Pointers überprüfen, ob er auf null zeigt. Oder man nimmt einfach die Adresse 0 dafür, und überprüft trotzdem. Oder man läßt die Überprüfung sein und erklärt den Effekt für undefiniert.

    Fakt ist: Man kommt um sowas nicht drumherum. In Lisp gibts NIL, in Eiffel gibts Void, in C# gibts void (diese drei Sprachen kennen keine expliziten Zeiger wie in C++).

    Mal ein Extrembeispiel ... in SML (funktionale Sprache) gibt es keine Zeiger (jedenfalls solange man im funktionalen Teil der Sprache bleibt). Es gibt nur Werte. Einen optionalen Wert stellt man mit einem OPTION-Typ dar (das ist ein polymorpher Typ, in etwa zu vergleichen mit C++-Templates ...):

    datatype 'a OPTION = NONE | SOME of 'a
    -- sei foo eine Funktion, die optional ein int zurückliefert
    -- dh vom Ergebnistyp int OPTION
    case foo () of
      SOME n => machwas mit n
    | NONE => kein Ergebnis behandeln
    

    (mein SML ist leicht rostig, mag sein dass die syntax irgendwo mal nicht ganz hinhaut)

    Sorry für das weite Ausholen, aber was glaubst du passiert, wenn ich in dem case den NONE-Zweig weglasse, und foo ein NONE zurückliefert? Eine Exception! Es geht schlicht nicht anders, die Optionalität bringt das mit sich. Der Unterschied zu C++ ist, dass man Exceptions abfangen kann, UB nicht.



  • Soweit ist mir der kleine Vorteil auch klar. Doch warum macht man nicht einen großen Vorteil und sagt:

    Jede Referenz zeigt auf ein Objekt. dh dass nicht NULL der Default ist, sondern dass ich, wenn ich

    Klasse obj;
    

    schreibe, wie in C++ der Standard Ctor aufgerufen wird.

    Das würde bedeuten: es gibt keine NULL-Zeiger, bzw. es gibt nur dann NULL-Zeiger wenn ich es explizit will:

    public static void foo(Klasse obj)
    {
    }
    //...
    foo(nil);
    

    Damit würden doch schon die meisten NullReferenceExceptions wegfallen.
    Und man würde das C Problem der uninitialisierten Variablen umgehen.

    Denn wenn diese Referenzen keine echten Zeiger sind, kann ich mir das explizite Speicherallokieren (obj=new Klasse();) sparen.

    Vielleicht bin ich blind und blöd, aber ich sehe den Vorteil von den ganzen Null-Referenzen nicht.

    Sicher, eine Null-Referenz ist besser als ein wilder Zeiger, aber was hindert die Sprachdesigner daran, generell keine uninitialisierten Referenzen zuzulassen, bzw. es explizit zu fordern wenn es denn unbedingt sein muss:

    Klasse foo; //ruft Ctor auf, bzw. ist verboten
    Klasse foo2=nil; //ruft keinen Ctor auf, sondern NULLt die Referenz
    Klasse foo3=new Klasse(); //ruft Ctor auf, ob foo3 oder foo besser ist, ist egal
    

    Vielleicht programmiere ich zu viel C++, aber in C# sind 70% meiner Fehler das Vergessen einer Initialisierung.



  • Wohin zeigt ein Zeiger wenn Du ihn in C++ anlegst? Genau irgendwohin, in Java ist das eine Nullreferenz, automatisch, das heißt Du kannst zumindest problemlos abfragen ob sich da ein gültiges Objekt dahinter verbirgt oder nicht.

    Der Rest ist doch reine Syntax:
    C++:

    Klasse obj;
    

    wird in Java zu:

    Klasse obj = new Klasse();
    

    Das ist weder ein Vor- noch ein Nachteil, das ist schlicht und einfach ein popliger syntaktischer Unterschied.

    Und mit

    Klasse obj;
    implizit das new durchzuführen halte ich nicht für allzugeschickt. Das sieht so harmlos aus und kann doch so teuer sein.



  • Shade Of Mine schrieb:

    Klasse obj;
    

    schreibe, wie in C++ der Standard Ctor aufgerufen wird.

    Das heißt jedesmal still und heimlich ein new?

    Das würde bedeuten: es gibt keine NULL-Zeiger, bzw. es gibt nur dann NULL-Zeiger wenn ich es explizit will

    OK, wär eine Möglichkeit. Wenn ich dich richtig interpretiere, willst du, dass Referenzen in Java/C# nicht automatisch mit null/void initialisiert werden, sondern mit frisch erzeugten Objekten.

    Und wozu? -->

    Damit würden doch schon die meisten NullReferenceExceptions wegfallen.
    Und man würde das C Problem der uninitialisierten Variablen umgehen.

    Also nur um Anfängerfehler zu vermeiden?

    Denn wenn diese Referenzen keine echten Zeiger sind, kann ich mir das explizite Speicherallokieren (obj=new Klasse();) sparen.

    Was würde das bringen? Explizites new ist doch gut, damit man sieht, wo Objekte erzeugt werden.

    Vielleicht programmiere ich zu viel C++, aber in C# sind 70% meiner Fehler das Vergessen einer Initialisierung.

    Anfängerfehler.



  • @Shade:
    Keine Ahnung, ob du zuviel C++ programmierst - geht das überhaupt? *gg*
    Jedenfalls ist, was dieses Thema angeht, C++ semantisch viel sauberer als Java. Der Ausdruck:

    T obj;
    

    Legt in C++ immer ein Objekt vom Typ T an, wobei ggf. Konstruktoren aufgerufen werden. In Java wird, falls T ein eingebauter Datentyp (z.B. int) ist, genauso verfahren. Ist T allerdings ein selbstdefinierter Typ, dann handelt es sich bei T um einen Pointer und der ist standardmäßig Null. Als C++-Programmierer ist man dieses semantische Kuddelmuddel einfach nicht gewohnt.

    Dein Vorschlag, daß, sollte T ein selbstdefinierter Typ sein der Pointer implizit mit einem neuen Objekt initialsiert wird, ist nicht durchführbar, da auch Java-Programmierer den Nullstatus von Pointern brauchen. Es muß z.B. möglich sein, Konstrukte wie

    // ...
    if ( obj == nil)  // ist das korrektes Java?
       {
       // ...
       }
    // ...
    

    zu verwenden, denke ich. Und erstrecht braucht man das bei der Definition von Klassen:

    class MyClass {
       T obj;
    };
    

    Ich glaube, die Sprache wäre nahezu unbrauchbar, wenn hier obj immer gleich mit einem geeigneten Objekt initialisiert würde.

    Stefan.



  • Implizites Anlegen finde ich auch nicht sonderlich elegant. Ich wäre wenn überhaupt dafür, dass man gezwungen wird eine Referenz zu initialisieren.

    Und der Vortiel vn Referenzen gegenüber Zeigern ist u.A. auch, dass man Sie nicht explizit dereferenzieren muss.



  • 1. Solche Nicht-Initialisierungen können bei Java zumindest nur sehr eingeschränkt auftreten. Das geht nur bei Membervariablen und bei Arrayelementen. Bei lokalen Variablen, wie bei

    import javax.swing.*;
    
    public class InitTest
    {
       public static void main (String [] args)
       {
          JFrame frame;
          frame.setVisible(true);
       }
    }
    

    kriegt man beim Kompilieren bereits folgenden Fehler:

    InitTest.java:8: variable frame might not have been initialized
          frame.setVisible(true);
          ^
    1 error
    

    2. Eine automatische Initialisierung führt IMHO zu Fehlern, die schwerer aufzufinden sind als simple NullPointerExceptions bei nichtinitialisierten Membervariablen. Diese Fehler sind schließlich dadurch gekennzeichnet, dass keine Fehlermeldung kommt, sondern der Code einfach nicht so funktioniert, wie er soll. Meistens will man so eine Variable schließlich nicht mit irgendeinem Standardobjekt initialisieren, sondern mit etwas speziellem.

    3. Eine automatische Initialisierung kostet Zeit und Speicher. Letztendlich muss man das richtige Objekt ja doch erzeugen und der Variable zuweisen. Der Rechner arbeitet dann also doppelt.



  • Ich schliesse mich Helium an

    gezwungene initialisierung von zeigern wie in java ist
    wesentlich besser fuer den programmierstil

    ausserdem erzaehlt dir java bei einer uninitialisierten referenz immer das da was nicht stimmen kann

    mach mal

    JPanel pan;
    

    der compiler verlangt zumindest

    JPanel pan = null;
    

    ich finde diese schreibweise besser und mache das auch in C++ immer so

    Ausserdem ist es gut fuer anfaengernur referenzen zu haben weil wieviele

    void function (Klasse Objekt)
    

    hab ich schon gesehen wo immer der copy constructor aufgerufen wird

    ich bin zB ein extremer Referenzen Nutzer in C++
    und benutze eigentlich fast nie zeiger - ausser wenn ich wirklich zeigerarithmetik brauche

    C:
    schlimm ist es wenn man da mal einen * vergisst
    dann springt man schon wie wild im speicher - das geht bei Java nicht

    auf der anderen seite ist das auch ein nachteil von java - man kann nicht wie wild im speicher herumhuepfen

    gomberl

    Nachtrag:
    @DStefan:
    wer sagt den das T ein objekt ist?
    kann ja genauso fuer int stehen - in C++ laesst sich ja viel mit den präprozessor anweisungen machen - der praeprozessor ist auch eines der besten dinge in C++ - was man da alles machen kann



  • gomberl schrieb:

    der praeprozessor ist auch eines der besten dinge in C++ - was man da alles machen kann

    Meinst du das ernst?! 😮 😮 😮



  • DStefan schrieb:

    Jedenfalls ist, was dieses Thema angeht, C++ semantisch viel sauberer als Java. Der Ausdruck:

    T obj;
    

    Legt in C++ immer ein Objekt vom Typ T an, wobei ggf. Konstruktoren aufgerufen werden. In Java wird, falls T ein eingebauter Datentyp (z.B. int) ist, genauso verfahren. Ist T allerdings ein selbstdefinierter Typ, dann handelt es sich bei T um einen Pointer und der ist standardmäßig Null. Als C++-Programmierer ist man dieses semantische Kuddelmuddel einfach nicht gewohnt.

    Genau umgekehrt. In Java ist das semantisch viel sauberer.
    T name;
    deklariert in Java immer eine Variable ohne ihr einen Wert zuzuweisen. Java verhält sich da bei primitiven Datentypen wie int oder double ganz genauso wie bei Variablen, die eine Objektreferenz enthalten.
    Das Kuddelmuddel hat man in C++. Denn dort wird z.B. bei einem Objekt automatisch ein neues Objekt erstellt und die Adresse der Variablen zugewiesen. Bei int oder double dagegen wird kein Objekt erzeugt sondern nur die Variable deklariert.

    Nochmal zur Klarstellung: Eine Variable kann niemals ein Objekt enthalten sondern immer nur eine Referenz oder einen Zeiger auf ein Objekt. Ein int dagegen ist kein Objekt und kann direkt in einer Variablen enthalten sein.



  • DrZoidberg schrieb:

    Genau umgekehrt. In Java ist das semantisch viel sauberer.
    T name;
    deklariert in Java immer eine Variable ohne ihr einen Wert zuzuweisen.

    Aber nur, wenn du 0 nicht als Wert anerkennst.

    Ich glaube die beiden Sprachen nehmen sich da nicht viel, es existiert in beiden Sprachen eine mehr oder weniger scharfe Trennung zwischen Variablen, die initialisiert werden müssen, und solchen, bei denen das unnötig ist bzw. automatisch passiert. In Java zwischen primitiven Typen und Referenztypen, in C++ zwischen POD und nicht-POD-Typen. Ironischerweise initialisieren sich in C++ die "höheren" Typen selbst, während das in Java die "niederen" tun 😉



  • Bashar schrieb:

    Aber nur, wenn du 0 nicht als Wert anerkennst.

    Das gilt auch wieder nur eingeschränkt auf Membervariablen und Arrayelemente:

    import javax.swing.*;
    
    public class InitTest
    {
       public static void main (String [] args)
       {
          int i;
          ++i;
       }
    }
    

    läßt sich z.B. nicht kompilieren, weil:

    InitTest.java:8: variable i might not have been initialized
          ++i;
            ^
    1 error
    

    Was man aber bei Java sagen kann, ist, dass eigentlich jede Variable initialisiert wird. Entweder automatisch mit null oder 0 oder manuell durch den Programmierer. Man kann kein Javaprogramm kompilieren, bei dem dies nicht der Fall ist.



  • DrZoidberg schrieb:

    Das Kuddelmuddel hat man in C++. Denn dort wird z.B. bei einem Objekt automatisch ein neues Objekt erstellt und die Adresse der Variablen zugewiesen. Bei int oder double dagegen wird kein Objekt erzeugt sondern nur die Variable deklariert.

    ich bin nicht einverstanden mit der trennung von objekt und variable, es gibt zwar einige implementierungs unterschiede aber auf der semantischen ebene fühlen sie sich gleich an

    DrZoidberg schrieb:

    Nochmal zur Klarstellung: Eine Variable kann niemals ein Objekt enthalten sondern immer nur eine Referenz oder einen Zeiger auf ein Objekt. Ein int dagegen ist kein Objekt und kann direkt in einer Variablen enthalten sein.

    quelle?



  • @Gregor: das ist am besten auszudruecken mit "gemischten gefuellen"

    ich komme urspruenglich aus der C und dann C++ Ecke und habe dort schon einige sehr intelligente dinge gesehen die durch den Praeprozessor gemacht wurden

    das das ganze nicht so intuitiv ist wie java ist schon klar

    im endeffekt werd ich wohl sentimental wenn ich an ihn denke, aber richtig eingesetzt ist der C++ praeprozessor schon was feines



  • RHBaum: Score -2, Offtopic, Troll


Log in to reply