Hi Community!
- Welche engl. oder deutsche Literatur könnt ihr empfehlen zum Umstieg
von C auf C++?
Dies können Bücher (engl., deutsch), Websites oder PDFs sein.
Anwendungsgebiet ist die Entwicklung von Software auf der Kommandozeile
in Linux oder Windows.
Bisher habe ich das Buch und den gleichnamigen Kurs von Ira Pohl
gefunden. Meinungen?
Das Kursmaterial https://www.coursera.org/learn/c-plus-plus-a ist
irgendwie nicht zu erhalten, obwohl sie es schreiben.
- Erwünscht ist (zudem) die Diskussion auf Assembler-Ebene der
grundlegenden Features (Konstruktoren, Referenzen, "new/delete", Klassen
und Vererbung, evtl. exceptions, etc.).
Oder: Mit welchen Features verlässt man den Compiler-Bereich und es wird
Assembler-Code erzeugt, der sich von C unterscheidet? Ich denke,
einfache Klassen an sich provozieren keinen (oder minim)
unterschiedlichen Code gegenüber C-structs?
Bestenfalls sollte in der Diskussion bzw. Analyse der
x86/x86-64-Befehlssatz in Intel-Syntax (!) verwendet worden sein.
Falls es im Mikrocontroller-Bereich derartige Literatur gibt, darf nur
der AVR 8-bit-Befehlssatz referenziert werden.
Freundliche Grüsse
Microwave89
Zur Sprache selbst würde ich The C++ Programming Language vom Erfinder
der Sprache empfehlen. Gibt's auch auf Deutsch, aber ich weiß nicht, ob
das was taugt.
Jonas S. schrieb:> - Erwünscht ist (zudem) die Diskussion auf Assembler-Ebene der> grundlegenden Features (Konstruktoren, Referenzen, "new/delete", Klassen> und Vererbung, evtl. exceptions, etc.).
Wie das implementiert ist, ist compiler- und plattformspezifisch. Warum
glaubst du, dass das für das Erlernen der Sprache wichtig ist?
> Oder: Mit welchen Features verlässt man den Compiler-Bereich und es wird> Assembler-Code erzeugt, der sich von C unterscheidet?
Was meinst du "verlässt man den Compiler-Bereich"? C und C++ sind
verschiedene Sprachen, natürlich generieren die Compiler
unterschiedlichen Code. Dadurch verlässt man aber nicht irgendeinen
Bereich.
> Ich denke, einfache Klassen an sich provozieren keinen (oder minim)> unterschiedlichen Code gegenüber C-structs?
Das hängt davon ab, was "einfache Klassen an sich" sein sollen. Wenn du
PODs meinst, dann hast du wahrscheinlich recht. Sobald aber
Konstruktoren, Vererbung, Polymorphie u.s.w. dazukommen, sieht's ganz
anders aus.
Allgemein: Danke für die schnellen Antworten, komme erst jetzt dazu, zu
antworten.
Rolf M. schrieb:> Warum> glaubst du, dass das für das Erlernen der Sprache wichtig ist?
Es hilft mir beim Erlernen und korrekten Anwenden, wenn ich verstehe,
wie ich mir x und y auf C- bzw. Assembler-Ebene vorstellen muss.
Ausserdem möchte ich immer gerne verstehen, wie etwas unter der Haube
funktioniert.
Rolf M. schrieb:> Was meinst du "verlässt man den Compiler-Bereich"?
Also welche Features erzeugen von C deutlich verschiedenen
Maschinencode?
Eben - Bei einer einfachen Klasse (ich weiss gerade (noch) nicht, was
POD ist) mit Gettern und Settern aber ohne Kon-/Destruktoren kann ich
mir vorstellen, dass grundsätzlich ähnlicher Code erzeugt wird wie für
ein C-struct mit u.a. Funktionszeigern darin. "new" und "delete"
allozieren intern irgendwo/-wie Speicher, z.B. mit malloc.
Bei Konstruktoren bzw. Destruktoren wird eine C-ähnliche Entsprechung in
Assembler/Maschinencode schon schwierig. Für mich wäre das wie eine
Funktion, die sich nicht an eine Standard-ABI hält, weil es da ja nicht
mal ein "void" als Rückgabe gibt.
Und dann richtig schwer - wie muss ich mir z.B. die &-Referenzen
vorstellen?
Ich kenne nur
1
voidfoo(intbar){}
2
voidfoo2(int*bar){}
3
...
4
foo(789);
5
foo(&blah);
, was ich mir zumindest in x86(-64)-Assembler gut vorstellen kann.
Wie soll jedoch
1
voidfoo(int&bar){}
in Assembler aussehen? Ich kann die Funktion noch nicht einmal auswendig
aufrufen... :/
Ihr "müsst" die Fragen nicht ohne Not beantworten, sie sollen aber die
Richtung zeigen, in die ich stutzig werde und Literaturtipps wünsche.
Rolf M. schrieb:> Sobald aber> Konstruktoren, Vererbung, Polymorphie u.s.w. dazukommen, sieht's ganz> anders aus.
Exakt dies dachte ich. Wahrscheinlich ist die beste Lösung erstmal, den
Output des Compilers (und Linkers) bei einem einfachen aber klar
objektorientierten Programm zu disassemblieren.
Raoul D. schrieb:> Wie wäre es damit:>> http://mozart.informatik.fh-kl.de/download/Lehre/WS1617/Prog2/Vorlesung/doc/ppcP9000.html
Habe nicht wirklich hineingeschaut, aber da steht eher etwas von
Java-Umstieg, und das ist bei mir schon länger her... ;)
Grüsse - Microwave
Wenn Du C++ lernen möchtest, dann möchtest Du offensichtlich klassisch
OOP lernen oder generische Programmierung - irgendetwas anderes
besonderes an C++? Prozedural kannst Du ja schon, weil Du von C kommst.
Daher würde ich mich auch damit beschäftigen, und die
Assembler-Umsetzung (zunächst) ausser acht lassen. Erst wenn Du das
verstanden hast, dann gehe an die Details ...
Jonas S. schrieb:> Rolf M. schrieb:>> Warum>> glaubst du, dass das für das Erlernen der Sprache wichtig ist?>> Es hilft mir beim Erlernen und korrekten Anwenden, wenn ich verstehe,> wie ich mir x und y auf C- bzw. Assembler-Ebene vorstellen muss.
Das hilft nicht, vergess es einfach ganz schnell. Ich unterstelle dir
jetzt einfach (nicht böse gemeint, sondern realistisch), dass du
(genauso wie selbst die meisten professionellen Programmierer) schon den
Befehlssatz einer einigermaßen aktuellen CPU nicht mehr sinnstiftend
verstehen.
Du möchtest mit C++ eine sehr leistungsfähige Programmiersprache lernen.
Dafür gibts sehr gute Compiler, die mehr von Assembler verstehen, als du
:-)
Was hilft: Mach dir klar, wo und wie Code erzeugt wird.
> Ausserdem möchte ich immer gerne verstehen, wie etwas unter der Haube> funktioniert.
Das wiederum ist ja legitim. Aber dazu brauchts kein Assembler.
>> Rolf M. schrieb:>> Was meinst du "verlässt man den Compiler-Bereich"?>> Also welche Features erzeugen von C deutlich verschiedenen> Maschinencode?> Eben - Bei einer einfachen Klasse (ich weiss gerade (noch) nicht, was> POD ist)
Plain-old-data. Im Prinzip das, was eine struct in C ist/war.
> mit Gettern und Settern
C++ kenn keine Getter und Setter.
> aber ohne Kon-/Destruktoren kann ich> mir vorstellen, dass grundsätzlich ähnlicher Code erzeugt wird wie für> ein C-struct mit u.a. Funktionszeigern darin.
Nein, wozu auch?
Bei einer einfachen Klasse gibt es nicht die geringste Notwendigkeit für
Funktionszeiger. Der Compiler wird nach wie vor einfache Funktionen
erzeugen, die dann vom Linker zur Compilezeit fest zusammengebunden
werden. Fertig. Wenn du magst, stell dir vor, dass diese Funktionen als
unsichtbares Argument immer den "this"-Zeiger haben.
Der Vergleich mit der C-Struktur mit Zeigern drin ist schwierig. In C
ist das eine Krücke, weil man quasi die "Methoden" zum "Objekt" packen
möchte. In C++ macht das im Normalfall nach wie vor der Linker zur
Compilezeit.
Erst bei Vererbung von Klassen kann man in C++ fordern, dass
Funktionen eben nicht zur Compilezeit gebunden werden, sondern erst zur
Laufzeit. Das nennt man dort "virtuelle Methode". Und für solche wird
dann tatsächlich eine kleine Struktur mit Funktionszeigern angelegt, die
sich gemeinhin "vtable" nennt.
> "new" und "delete"> allozieren intern irgendwo/-wie Speicher, z.B. mit malloc.
Ja, die haben i.d.R. sogar irgendwo eine Implementierung.
> Bei Konstruktoren bzw. Destruktoren wird eine C-ähnliche Entsprechung in> Assembler/Maschinencode schon schwierig. Für mich wäre das wie eine> Funktion, die sich nicht an eine Standard-ABI hält, weil es da ja nicht> mal ein "void" als Rückgabe gibt.
Warum? Der Konstruktor ist einfach nur eine Funktion mit "void als
Rückgabe", wie du es schreibst. Auch diese Funktion kriegt, wenn du so
magst, als erstes Argument "this" übergeben und soll dann zusehen, wie
sie "this" sinnvoll initialisiert. Der Speicher, auf den "this" zeigt,
wurde zuvor ja schon vom new-Operator angefordert.
> Und dann richtig schwer - wie muss ich mir z.B. die &-Referenzen> vorstellen?> Ich kenne nurvoid foo(int bar){}> void foo2(int* bar){}> ...> foo(789);> foo(&blah);, was ich mir zumindest in x86(-64)-Assembler gut vorstellen> kann.> Wie soll jedochvoid foo(int &bar){}in Assembler aussehen? Ich kann die> Funktion noch nicht einmal auswendig> aufrufen... :/
Warum nicht?
Es ist völlig wurscht, wie der Compiler eine Referenz übersetzt. Eine
Referenz ist nur ein zweiter Name für ein Objekt. In vielen Fällen
könntest du das sogar durch ein prähistorisches Präprozessormakro
(#define) ersetzen.
> Rolf M. schrieb:>> Sobald aber>> Konstruktoren, Vererbung, Polymorphie u.s.w. dazukommen, sieht's ganz>> anders aus.>> Exakt dies dachte ich. Wahrscheinlich ist die beste Lösung erstmal, den> Output des Compilers (und Linkers) bei einem einfachen aber klar> objektorientierten Programm zu disassemblieren.
Das ist eine ziemlich absurde Idee.
Welchen Erkenntnisgewinn versprichst du dir denn davon? Ich meine, du
kannst ja gerne dem Optimierer beim Arbeiten zugucken und wirst alsbald
feststellen, dass der Optimierungen kennt und anwendet, die du auch nach
drei Flaschen Lambrusco noch nicht nachvollziehen kannst :-)
Du wirst feststellen, dass der Linker immer noch genauso linkt, wie
früher. Nur die Namen der Funktionen sehen etwas anders aus, weil wegen
der Überladungen noch eine Signatur dazukommt. Das passiert unter dem
Arbeitstitel "name mangling".
Les doch einfach mal Wikipedia-Artikel zu einigen Stichworten in meiner
Antwort. Wenn du ja schon Hintergrund in C hast, sollte das dir einen
Überblick verschaffen.
Jonas S. schrieb:> Allgemein: Danke für die schnellen Antworten, komme erst jetzt dazu, zu> antworten.>> Rolf M. schrieb:>> Warum>> glaubst du, dass das für das Erlernen der Sprache wichtig ist?>> Es hilft mir beim Erlernen und korrekten Anwenden, wenn ich verstehe,> wie ich mir x und y auf C- bzw. Assembler-Ebene vorstellen muss.> Ausserdem möchte ich immer gerne verstehen, wie etwas unter der Haube> funktioniert.
Die Idee einer Hochsprache ist aber gerade, dass man das nicht braucht.
Ich würde dir empfehlen, dich erstmal nur auf der C++-Ebene zu bewegen,
und wenn du verstanden hast, wie die Sprache funktioniert und zwecks
Optimierung genauer bescheid wissen willst, dann tu das danach.
Das Problem wird auch sein, dass du die modernen aggressiven Optimizer
das, was generiert wird, für Menschen recht schwer verständlich wird.
Und schaltet man den Optimizer aus, hat man keine realistische
Einschätzung mehr.
> Rolf M. schrieb:>> Was meinst du "verlässt man den Compiler-Bereich"?>> Also welche Features erzeugen von C deutlich verschiedenen> Maschinencode?
Wie gesagt: Das hängt sehr vom Compiler ab, auch wenn es einige
"gängige" Implementationen gibt. Früher(tm) haben C++-Compiler C-Code
generiert. Heute erzeugen sie aber in der Regel direkt Assembler-Code,
weil das besseres Optimierungspotenzial bietet. Ich glaube Comeau C++
kann noch nach C konvertieren, kostet aber was und ist anscheinend seit
2013 nicht mehr sonderlich aktiv.
> Eben - Bei einer einfachen Klasse (ich weiss gerade (noch) nicht, was> POD ist)
"plain old data", also etwas, das im Prinzip genauso wie eine C-Struktur
aufgebaut ist. Bekommt man, wenn die Klasse keine Kontruktoren und
keinen Destruktor hat, keine Basisklassen, keine Member-Variablen, die
selbst nicht POD sind und keine virtuellen Memberfunktionen.
> mit Gettern und Settern aber ohne Kon-/Destruktoren kann ich> mir vorstellen, dass grundsätzlich ähnlicher Code erzeugt wird wie für> ein C-struct mit u.a. Funktionszeigern darin.
Nein, Funktionszeiger werden da nicht angelegt. Nur die Variablen sind
enthalte und bei virtuellen Memberfunktionen ein oder mehrere
Vtable-Zeiger.
> "new" und "delete" allozieren intern irgendwo/-wie Speicher, z.B. mit> malloc.
Ja, allerdings wirft new eine Exception, wenn kein Speicher mehr da ist.
> Bei Konstruktoren bzw. Destruktoren wird eine C-ähnliche Entsprechung in> Assembler/Maschinencode schon schwierig. Für mich wäre das wie eine> Funktion, die sich nicht an eine Standard-ABI hält, weil es da ja nicht> mal ein "void" als Rückgabe gibt.
Da wird noch einiges außenrum gemacht. Schließlich müssen auch die
Konstruktoren / Destruktoren der Basisklassen und Membervariablen
aufgerufen und verschiedene weitere Initialisierungen gemacht werden.
> Und dann richtig schwer - wie muss ich mir z.B. die &-Referenzen> vorstellen?
Die sind das einfachste. Die Compiler, die ich kenne, implementieren das
unter der Haube einfach genau wie Zeiger.
> Rolf M. schrieb:>> Sobald aber>> Konstruktoren, Vererbung, Polymorphie u.s.w. dazukommen, sieht's ganz>> anders aus.>> Exakt dies dachte ich. Wahrscheinlich ist die beste Lösung erstmal, den> Output des Compilers (und Linkers) bei einem einfachen aber klar> objektorientierten Programm zu disassemblieren.
Wie gesagt: Ich halte es für wenig sinnvoll, so vorzugehen. C++ selbst
ist schon schwierig genug, da muss man sich nicht zusätzlich auch noch
aufhalsen, den erzeugten Code verstehen zu wollen.
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang