Warum programmiert ihr in C?



  • C++ ist eine übermüllte scheisse ... bei dem man mehr Abstraktion und Sicherheit gewinnen wollte und doch nichts erreichte hat, was der C Programmierer schon gar nicht braucht! Ihr C++' er kommt immer mit Argumenten die einfach nicht überzeugen!
    Und das letzte Argument von Volkard ist gar nicht nennenswert.



  • Ausnahmekoenner schrieb:

    ... Aber was meinst Du, müsste wohl eine (hypothetische) DAU-sichere std::string::TranslateMessage(char** Buffer) Funktion tun? ..

    Meinst du das ernst?
    Du vergleichst also die sichere C++ Variante mit der unsicheren C Variante?
    Oder gehst du davon aus, dass C++'ler so degeneriert sind, dass sie nicht miteinander kommunizieren können und deshalb die erste Variante nicht in Frage kommt?



  • Sie ist nur so unsicher wie der Programmierer der schreibt!



  • knivil schrieb:

    Wir hatten schon lange keinen so langen Thread C vs. C++. Fuer gewoehnlich sind sie im C++ Bereich zu finden. Und nicht nur das, allein die Qualitaet der Beitraege sucht seines Gleichen.

    siehste, hat c mehr Kante als c++ bzw. c-- 😃



  • Ausnahmekoenner schrieb:

    volkard schrieb:

    Aber in C müssen alle Funktionen auf allen Ebenen mit if ihre Kinder überprüfen.

    Und genau das ist der grundlegende Irrtum. Muss man nämlich nicht. Und das hat auch nichts mit unsauberem Programmierstil zu tun. Wenn z.B. Josef so eine geniale Funktion erfindet, wie diese hier:

    void TranslateMessage(char** Buffer){
    /* ... */
    

    und dabei Hans die (mündliche) Zusage gibt, dass TranslateMessage garantiert funktioniert, wenn Buffer nicht NULL und mindestens BUFSIZE groß ist

    Das ist klar.
    Aber sauviele Funktionen werden am Ende abgefragt werden müssen, weil innendrin oder in deren Kinder doch irgendwo syscalls lauern. Hast Du schonmal versucht, die *notwenignen* ifs alle einzubauen, statt sie wie üblich schlicht zu ignorieren?

    Ich rede nicht von unsinnigen Tests. Die sinnigen Tests. Die Notwendigen. Die, die schiefgehen können, weil eine Datei verschwunden ist, ein Puffer nicht allokiert werden konnte, ein Thread nicht erstellt werden konnte, eine Schriftart nicht geladen werden konnte und so weiter...

    Und von den sinnigen Tests gibt es noch ausreichend viele. Und die haben wir in C++ zu Nullkosten, aber in C muß man dafür zahlen. Also kann man in C++ schnelleren Code bauen als in C.



  • Das mit den zero-cost-exceptions geht in C auch:

    #define QUOTEME_(x) #x
    #define QUOTEME(x) QUOTEME_(x)
    #define THROW(msg) { fprintf(stderr, "%s in " __FILE__ ": " QUOTEME(__LINE__) "\
    \n",(msg));exit(255);}
    

    Wenn ein Fehler auftritt muss das Programm sowieso beendet werden.

    Du wirst wahrscheinlich argumentieren, der Rückgabewert muss zuerst erst mit einem if der abgefragt werden und erst dann kann die Ausnahme geworfen (bzw. das Programm beenden) werden => zusätzliche Kosten.

    Aber ich bin mir sicher, std::basic_fstream wurde irgendwie so geschrieben:

    void // Rückgabewert auf seperater Zeile ist in C++ Pflicht
    basic_ifstream::init(__filebuf_type ft)
    {
      // Aufruf der C-Library des Systems um die Datei zu öffnen
      // Man beachte die schicke C++-Style-Kommentare
      // ...
      if (native_file_handle________ == 0)
        this->setstate(this_type________::failbit) // ruft eine komplexe Funktion auf,
                                                   // die testet ob this->exceptions() auf
                                                   // this_type________::failbit gesetzt ist und
                                                   // wird dann eventuell eine exception werfen.
    }
    

    Ich will gar nicht nachschauen, welche Member alles in std::fstream implementiert sind, wenn der insgesamt eine Grösste von sizeof(std::fstream)=284 aufweist, wovon allein std::basic_streambuf<_CharT, _Traits> folgende enthält:

    __c_lock                  _M_lock;
          __file_type               _M_file;
          ios_base::openmode        _M_mode;
          __state_type              _M_state_beg;
          __state_type              _M_state_cur;
          __state_type              _M_state_last;
          char_type*                _M_buf;         
          size_t                    _M_buf_size;
          bool                      _M_buf_allocated;
          bool                      _M_reading;
          bool                      _M_writing;
          char_type                 _M_pback;
          char_type*                _M_pback_cur_save;
          char_type*                _M_pback_end_save;
          bool                      _M_pback_init;
          const __codecvt_type*     _M_codecvt;
          char*                     _M_ext_buf;
          streamsize                _M_ext_buf_size;
          const char*               _M_ext_next;
          char*                     _M_ext_end;
    

    Das alles für nll Funktionalität, versteht sich.

    Folglich gilt für die files wie für die ausgabe wie für die strings wie für die gesamte Standardbibliothek folgendes: Sie ist zwar idiotensicher, aber inperformant. Lassen wir sie also für die weiteren Betrachtungen ausser acht.

    Wie werden in C++ Threads implementiert? Man schreibt Wrapper um die Betriebssystemfunktionen in C.
    Dann ist irgendwo ein C-Style Test
    // initialisiere this->native_thread_ptr________ mit einem nativen Thread Pointer der offiziellen Funktion des Betriebssystems

    if (this->native_thread_ptr________ == nullptr) // Jeder fähige C++-Programmierer hat irgendwo den templatigen nullptr-Trick liegen.
    { // neue Klammern gehören auf eine neue Zeile, alles andere ist Oracle Java-Style
      throw get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception("Oh sorry, "
    "the Thread creation failed, so I have to throw the "
    "get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception "
    "exception"); // hier liegt ein unhandlicher C-Style-String im Speicher, weil 
    // C++-Style-Strings noch mehr Overhead verursachen
    }
    

    Der Test ist in die Standard-Bibliothek ausgelagert, eine mündliche Abmachung zwischen dem Betriebssystementwickler und dem Ersteller der geschwindigkeitskritischen Anwendung ist verunmöglicht.

    volkard schrieb:

    Die, die schiefgehen können, weil eine Datei verschwunden ist, ein Puffer nicht allokiert werden konnte, ein Thread nicht erstellt werden konnte, eine Schriftart nicht geladen werden konnte und so weiter...

    Datei habe ich oben gezeigt, den Thread auch und wenn eine Schriftart geladen wird gelten die vernichtenden Ausführungen immer noch.

    DENN: Gtkmm, als einzige vernünftige C++ Komponentenbibliothek ist ein Wrapper von einer C-Library, Qt ist Performance-Bremse pur, wxWidgets kenne ich nicht (soll von Makros verseucht sein).

    C wird verwendet um Bibliotheken zu schreiben, die von mehreren Sprachen benutzt werden können sollen, C++ bietet manchmal eine Wrapper dafür an.

    Während du C folgendes schreibst, was zu deinem prophezeiten Overhead führt:

    void foo(struct s *x) {
      // ...
      if (fail) { free(allocated_memory); return 1; 
      return 0;
    }
    
    // Aufruf:
    if (foo(&x)) { /*  do exception handling */ }
    

    würdest du in in C++ das schreiben:

    void foo(s& x) // keine klassen, unnötiger overhead
    {
      // ...
      if (fail)
      {
        // der Speicher wird vom Destruktor weggeräumt
        throw oops_i_fail_exception("..."); // <- hier steht eine ausführliche Problembeschreibung
      }
    // ein return brauchts nicht, weil wir ja keinen succeed-Wert mehr haben
    }
    
    Aufruf:
    foo(x)
    

    Der fähige C-Programmierer übermittelt aber mündlich, was genau noch weggeräumt werden muss und dann sieht es so aus:

    void foo(struct s *x) {
      // ...
      return fail;
    }
    
    // Aufruf:
    if (foo(&x)) { free(allocated_memory); /* do exception handling */ }
    
    // ich bin mir sicher, dass foo() jetzt _nicht_ fehlschlagen wird:
    foo(&x); // ich habe eine überflüssige if-Abfrage vermieden, die in C++ gemacht wurde
    

    Wie genau hast du das mit den exceptions gemeint?



  • Wenn ein Fehler auftritt muss das Programm sowieso beendet werden.

    Danach hab ich aufgehört zu lesen.

    Wirklich unfassbar, wie wenig Ahnung die Cler von den C++ Konzepten haben.



  • Cönner schrieb:

    Das mit den zero-cost-exceptions geht in C auch:

    #define QUOTEME_(x) #x
    #define QUOTEME(x) QUOTEME_(x)
    #define THROW(msg) { fprintf(stderr, "%s in " __FILE__ ": " QUOTEME(__LINE__) "\
    \n",(msg));exit(255);}
    

    Wenn ein Fehler auftritt muss das Programm sowieso beendet werden.

    Nicht unbedingt. Außerdem hast Du so keinerlei close oder flush oder gracefully disconnet oder sowas gemacht. Um das hinzukriegen, mußt Du doch die Kosten zahlen.
    Können wir uns darauf einigen, daß man ein Programm sinnvollerweise in recht viele kleine Funktionen zerlegt? Du mußt das return 255 (nicht exit!) den ganzen Aufrufbaum entlang hochreichen bis zur main, um überall die offenen Sachen schließen zu können. Deswegen brauchst Du über den ganzen Aufrufbaum hinweg die bösen ifs (mal das free weggelassen, das geschieht in C++ ja auch).

    tuwas();//sicher fehlerfrei
    if(!(rc=subfunktion(...)) return rc;//hier kann was schiefgehen
    tuwas();//sicher fehlerfrei
    

    In C++ reicht

    tuwas();//sicher fehlerfrei
    subfunktion(...);//hier kann was schiefgehen
    tuwas();//sicher fehlerfrei
    

    Das sind in C Mehrkosten, die Du nicht leugnen kannst.
    OK, Du kannst sagen, daß Qt lahm wäre oder sowas. Aber der Hauptsatz der C++-Hasser, daß C++ vom Sprachdesign her prinzipiell langsamer wäre, ist hinfällig. Das Gegenteil ist sogar der Fall. C ist prinzipiell langsamer.



  • Cönner schrieb:

    Aber ich bin mir sicher, std::basic_fstream wurde irgendwie so geschrieben:
    ...
    Folglich gilt für die files wie für die ausgabe wie für die strings wie für die gesamte Standardbibliothek folgendes: Sie ist zwar idiotensicher, aber inperformant. Lassen wir sie also für die weiteren Betrachtungen ausser acht.

    Ich bin nicht glücklich mit der stream-Bibliothek. Deswegen verwende ich sie privat auch nicht. Versuch doch mal, std::sort zu schlagen. Dann weißt Du, daß da auch brutale Geschwindigkeit zu finden ist.

    Cönner schrieb:

    Wie werden in C++ Threads implementiert? Man schreibt Wrapper um die Betriebssystemfunktionen in C.
    Dann ist irgendwo ein C-Style Test
    // initialisiere this->native_thread_ptr________ mit einem nativen Thread Pointer der offiziellen Funktion des Betriebssystems

    if (this->native_thread_ptr________ == nullptr) // Jeder fähige C++-Programmierer hat irgendwo den templatigen nullptr-Trick liegen.
    { // neue Klammern gehören auf eine neue Zeile, alles andere ist Oracle Java-Style
      throw get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception("Oh sorry, "
    "the Thread creation failed, so I have to throw the "
    "get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception "
    "exception"); // hier liegt ein unhandlicher C-Style-String im Speicher, weil 
    // C++-Style-Strings noch mehr Overhead verursachen
    }
    

    Der Test ist in die Standard-Bibliothek ausgelagert, eine mündliche Abmachung zwischen dem Betriebssystementwickler und dem Ersteller der geschwindigkeitskritischen Anwendung ist verunmöglicht.

    Hä? Was soll das? Was wird da verunmöglicht? Den einen Test brauchen wir beide. Oder willst Du mündlich abmachen, daß es grundsätzlich möglich ist, einen neuen Thread anzulegen? In C++ ist es nur diese eine if. In C sind es alle ifs den Aufrufbaum hinauf bis zur main().

    Cönner schrieb:

    volkard schrieb:

    Die, die schiefgehen können, weil eine Datei verschwunden ist, ein Puffer nicht allokiert werden konnte, ein Thread nicht erstellt werden konnte, eine Schriftart nicht geladen werden konnte und so weiter...

    Datei habe ich oben gezeigt, den Thread auch und wenn eine Schriftart geladen wird gelten die vernichtenden Ausführungen immer noch.

    Welche vernichtenden Ausführungen?

    Cönner schrieb:

    Während du C folgendes schreibst, was zu deinem prophezeiten Overhead führt:

    void foo(struct s *x) {
      // ...
      if (fail) { free(allocated_memory); return 1; }
      return 0;
    }
    
    // Aufruf:
    if (foo(&x)) { /*  do exception handling */ }
    

    würdest du in in C++ das schreiben:

    void foo(s& x) // keine klassen, unnötiger overhead
    {
      // ...
      if (fail)
      {
        // der Speicher wird vom Destruktor weggeräumt
        throw oops_i_fail_exception("..."); // <- hier steht eine ausführliche Problembeschreibung
      }
    // ein return brauchts nicht, weil wir ja keinen succeed-Wert mehr haben
    }
    
    Aufruf:
    foo(x)
    

    Der fähige C-Programmierer übermittelt aber mündlich, was genau noch weggeräumt werden muss und dann sieht es so aus:

    void foo(struct s *x) {
      // ...
      return fail;
    }
    
    // Aufruf:
    if (foo(&x)) { free(allocated_memory); /* do exception handling */ }
    
    // ich bin mir sicher, dass foo() jetzt _nicht_ fehlschlagen wird:
    foo(&x); // ich habe eine überflüssige if-Abfrage vermieden, die in C++ gemacht wurde
    

    Wie genau hast du das mit den exceptions gemeint?

    Verstehe ich nicht. Welches if(fail)? Dein C++-Code hat doch weder Hand noch Fuß. Klassen erzeugen außerdem keinen Overhead.



  • Die erste Aussage ist misslungen, entschuldigt.

    std::sort schlagen, meinst du mit qsort? C hat eine kleinere Standardbibliothek als C++, deshalb verwende ich zusätzlich die GNU C Library, welche das mindestens so schnell kann (habs nicht gemessen).

    volkard schrieb:

    Cönner schrieb:

    // initialisiere this->native_thread_ptr________ mit einem nativen Thread Pointer der offiziellen Funktion des Betriebssystems
    if (this->native_thread_ptr________ == nullptr) // Jeder fähige C++-Programmierer hat irgendwo den templatigen nullptr-Trick liegen.
    { // neue Klammern gehören auf eine neue Zeile, alles andere ist Oracle Java-Style
      throw get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception("Oh sorry, "
    "the Thread creation failed, so I have to throw the "
    "get_boost_from_this_boosty_library::i_can_t_create_the_thread_exception "
    "exception"); // hier liegt ein unhandlicher C-Style-String im Speicher, weil 
    // C++-Style-Strings noch mehr Overhead verursachen
    }
    

    Der Test ist in die Standard-Bibliothek ausgelagert, eine mündliche Abmachung zwischen dem Betriebssystementwickler und dem Ersteller der geschwindigkeitskritischen Anwendung ist verunmöglicht.

    Hä? Was soll das? Was wird da verunmöglicht?

    Wird nachher beschrieben. Die zentrale Aussage ist jedoch die folgende (Hauptsatz):
    Der Test, welche man in C mit dem Rückgabewert macht, wird in C++ von der Funktion ausgeführt. Es ist also keine Geschwindigkeitseinbusse, ein if zu verwenden. Und weglassen ist in C++ nicht möglich.

    volkard schrieb:

    Welches if(fail)?

    Mir fehlt ein typisches Beispiel für C++ exceptions.
    Aus dem Kopf ist mir keines eingefallen und in http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.2 schreiben die nur folgendes hin:

    void f10()
     {
       ...
       if (...some error condition...)
         throw some_exception();
       ...
     }
    

    Ich will nur sagen: if (...some error condition...) kann man in C bei Bedarf weglassen, in C++ ist es immer da.



  • Cönner schrieb:

    std::sort schlagen, meinst du mit qsort? C hat eine kleinere Standardbibliothek als C++, deshalb verwende ich zusätzlich die GNU C Library, welche das mindestens so schnell kann (habs nicht gemessen).

    Dann probier mal ein halbwegs generisches Sort in C zu schreiben. Es muss nicht so toll wie std::sort sein, aber wenigstens ein bisschen generisch sein, so dass ich nicht das komplette sort selber schreiben muss wenn ich etwas sortieren will.

    Der Test, welche man in C mit dem Rückgabewert macht, wird in C++ von der Funktion ausgeführt. Es ist also keine Geschwindigkeitseinbusse, ein if zu verwenden. Und weglassen ist in C++ nicht möglich.

    Das 1. if - ja. Aber du musst in C ja ueberall testen ob es erfolgreich war, da jede Funktion ja einen Fehler liefern kann. Du musst den Fehler ja per return durchreichen bis du ihn behandeln kannst. Das ist in C++ eben schneller, da du keine ifs hast und Compilermagie hier das erledigt.



  • Cönner schrieb:

    std::sort schlagen, meinst du mit qsort? C hat eine kleinere Standardbibliothek als C++, deshalb verwende ich zusätzlich die GNU C Library, welche das mindestens so schnell kann (habs nicht gemessen).

    Ist da ein qsort, das für jeden Vergleich eine Vergleichsfunktion aufrufen muß?
    Falls ja: Das müssen wir in C++ nicht. Schneller.
    Falls nein: Das muß wohl mal ausgemessen werden.
    [/quote]

    Cönner schrieb:

    Der Test, welche man in C mit dem Rückgabewert macht, wird in C++ von der Funktion ausgeführt. Es ist also keine Geschwindigkeitseinbusse, ein if zu verwenden.

    Klar. Die Funktionen zwischen hier un der main() brauchen das if aber nicht.

    Cönner schrieb:

    Und weglassen ist in C++ nicht möglich.

    Klar.
    Beispiel new(nothrow)

    Cönner schrieb:

    Ich will nur sagen: if (...some error condition...) kann man in C bei Bedarf weglassen, in C++ ist es immer da.

    Da bin ich mir nicht so sicher.



  • Shade Of Mine schrieb:

    Dann probier mal ein halbwegs generisches Sort in C zu schreiben.

    Hast du das nicht gesehen? `void *array' ist generisch.

    Shade Of Mine schrieb:

    Das 1. if - ja. Aber du musst in C ja ueberall testen ob es erfolgreich war, da jede Funktion ja einen Fehler liefern kann.

    Wenn ich sicher bin, dass in der einen Funktion kein Fehler auftritt, kann ich mir das if sparen, in C++ geht das nicht

    Meistens ist nur ein if vorhanden, das gibt schonmal den entscheidenden Geschwindigkeitsvorteil. Sobald mehrere if's vorhanden sind, benötigen C und C++ gleich lang (das letzt if wird gespart und beim Aufrufer hingeschrieben), ein weiterleiten der Fehler ist aber auch in C möglich.



  • volkard schrieb:

    Ist da ein qsort, das für jeden Vergleich eine Vergleichsfunktion aufrufen muß?
    Falls ja: Das müssen wir in C++ nicht. Schneller.
    Falls nein: Das muß wohl mal ausgemessen werden.

    volkard schrieb:

    Klar. Die Funktionen zwischen hier un der main() brauchen das if aber nicht.

    Weiterleiten ist in C++ schneller, das gestehe ich ein. Ich sehe nur nicht ein, wo das (in C) so häufig gebraucht wird, dass es ein Problem wäre

    volkard schrieb:

    Beispiel new(nothrow)

    In dem Fall schon (die hab ich vergessen), aber für jede Funktion eine nothrow-Überladung zu schreiben, bläht die Binary nur unnötig auf und ist so umständlich, dass das normalerweise beiseite geschoben wird.



  • Ups, das erste Zitat (Performanz von qsort) habe ich gar nicht beantwortet.
    Also ja, es muss jedesmal eine Funktion ausgeführt werden, die Sortierung ist aber `arbitrary' (für Standardtypen gibts natürlich ne andere).
    Und in C++ muss auch immer ein Funktionsobjekt aufgerufen werden (im Notfall kann es mit __force_inline ein ganz kleines bisschen schneller sein als mein qsort)

    Was bedeutet das

    volkard schrieb:

    Da bin ich mir nicht so sicher.



  • Spielst du auf ausnahmesicheren Code nach der Abfrage an?



  • Cönner schrieb:

    Was bedeutet das

    volkard schrieb:

    Da bin ich mir nicht so sicher.

    Das war ein Euphemismus für: Nein.



  • Cönner schrieb:

    volkard schrieb:

    Klar. Die Funktionen zwischen hier un der main() brauchen das if aber nicht.

    Weiterleiten ist in C++ schneller, das gestehe ich ein. Ich sehe nur nicht ein, wo das (in C) so häufig gebraucht wird, dass es ein Problem wäre

    Es muß immer weitergeleitet werden. Insofern ist es ein Problem.
    Aber auch die paar ifs kosten in der Praxis nicht gewaltig viel Zeit. C ist natürlich nicht *deutlich* langsamer. Ich werde nichts von einem Faktor 30 erzählen, wie andere hier.
    Aber es ist prinzipiell ein kleines kleines Bißchen langsamer. Nicht schneller.

    Das Sortieren kann ich gerade nicht ausmessen. Ich habe da was laufen, das bis morgen früh braucht. Wenn man böse zu C sein will, läßt man wohl am besten ein von rand() gefülltes int-Array nach dem letzten Byte sortieren.



  • OMG Volkard lass es einfach. 🙄 Auch du wirst hier niemanden mehr das missratene C++ schmackhaft machen. C++ ist genauso angenehm wie Durchfall. Es gibt unter den bekannten Progammiersprachen kaum einen größeren Schrott als C++. Wenn du irgendwann mit dem Studium fertig bist und vielleicht mal in der realen Welt ein Projekt abbekommst dann wähle bitte nicht C++ als erste Sprache.



  • Cönner schrieb:

    In dem Fall schon (die hab ich vergessen), aber für jede Funktion eine nothrow-Überladung zu schreiben, bläht die Binary nur unnötig auf und ist so umständlich, dass das normalerweise beiseite geschoben wird.

    Es ist aber auch sehr selten, daß man einen syscall nicht testen muß, ob er geklappt hat. Aber auch da bin ich mit meinen Klassen nicht verbohrt. Zum Beispiel bin ich so frech, niemals zu prüfen, ob ein CloseHandle funktioniert hat (das muß aber unser kleines Geheimnis bleiben, wenn Du das im C++-Forum ausplauderst, werde ich vielleicht gesteinigt).
    Das Aufblähen hier ist als ein space-time tradeoff zu betrachten. Das zero-cost exception handling übrigens auch. Für PCs empfehle ich es. Für embedded Kisten mit arg wenig Speicher natürlich nicht.


Anmelden zum Antworten