42
Software ubiquitärer Systeme (SuS) Anwendungsentwicklung in C/C++ https://ess.cs.tu-dortmund.de/DE/Teaching/SS2018/SuS/ Olaf Spinczyk [email protected] https://ess.cs.tu-dortmund.de/~os AG Eingebettete Systemsoftware Informatik 12, TU Dortmund

SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

Software ubiquitärer Systeme (SuS)

Anwendungsentwicklung in C/C++

https://ess.cs.tu-dortmund.de/DE/Teaching/SS2018/SuS/

Olaf Spinczyk

[email protected]://ess.cs.tu-dortmund.de/~os

AG Eingebettete SystemsoftwareInformatik 12, TU Dortmund

Page 2: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 2

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

HardwareHardware

BetriebssystemBetriebssystem

MiddlewareMiddleware

DatenhaltungDatenhaltung

Anwendung/ProgrammierungAnwendung/Programmierung

Page 3: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 3

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

Page 4: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 4

Verbreitung von C und C++● Statistik über den Einsatz von Programmiersprachen bei der

Entwicklung eingebetteter Systeme– Daten beruhen auf einer Umfrage bei 74 Unternehmen der Branche

C

C++

Assembler

Java

.Net

SystemC

IEC 61131

4GL-Sprachen

DSL

sonstige

0 10 20 30 40 50 60 70

5858

5858

4343

4242

3232

1818

1818

1212

77

88

Einsatz in %

Quelle: Computer Zeitung Nr. 23, Juni 2009, S. 16

Page 5: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 5

Warum C und C++? (1)● Transparency and Control [1]● Beispiele:

– Datenstrukturen wie Arrays von Structs werden einfach linear im Speicher abgelegt. Zugriffe sind durch Cache-Lokalität sehr schnell.

● In Java bestimmen der Compiler und das Laufzeitsystem die Objektplatzierung (keine Transparenz).

– C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet● In Java wird von der realen Hardware abstrahiert.

– C-Programme verwalten den Heap-Speicher manuell.Dies gibt dem Programmierer mehr Kontrolle über die Performance.

● Java arbeitet mit einem Garbage Collector. Die Strategie bleibt dem Laufzeitsystem überlassen.

● C/C++-Programme …– lassen sich manuell optimieren. Man sieht ihnen die verursachten Kosten

direkt an.– sind in der Regel effizienter als zum Beispiel Java-Programme.

Page 6: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 6

Warum C und C++?● Vergleich beim „Great Programming Language Shootout“

– siehe http://shootout.alioth.debian.org/

– C-Code hier bis zu einem Faktor von 36 schneller als Java („mandelbrot“-Benchmark) und bis zu 23 mal kleiner.

● Mit JIT-Compiler ist Java deutlich schneller (als mit Interpreter),aber auch deutlich größer.

– C und C++ liegen dicht beieinander. C-Code war etwas langsamer, dafür aber kleiner als der äquivalente C++-Code.

Page 7: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 7

Und warum lieber nicht?● Undefiniertes Verhalten, z.B.

– Zugriffe über die Grenzen eines Arrays hinweg– Lesen einer uninitialisierten Variablen– Dereferenzieren eines Zeigers, der auf kein gültiges, zum Typ des Zeigers

passendes Objekt verweist● Manuelle Heap-Speicher-Verwaltung, z.B.

– Memory Leaks– Ungültige Zeiger– Mehrfachfreigabe

● Portabilität, z.B.– Wertebereich von Objekten variiert je nach Compiler/Zielplattform

➔ Stabilitäts- und Sicherheitsprobleme, z.B.– Buffer Overflows können genutzt werden, um die Kontrolle über privilegierte

Programme zu übernehmen.

Page 8: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 8

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

Page 9: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 9

MISRA-C [2] (1)(Motor Industry Software Reliability Association)

● Über 100 Programmierregeln der Automobilindustrie– Vermeidung von undefiniertem Verhalten– Einhaltung des Standards (ISO C)– Guter Programmierstil zur Vermeidung von Fehlern und Missverständnissen

● Werkzeugunterstützung zur Überprüfung der Einhaltung– „MISRA-Checker“– Statische Code-Analyse

● Weite Verbreitung in der Industrie– Compiler-Hersteller integrieren Checker– Integratoren verlangen die Einhaltung der Regeln von Zulieferern

Page 10: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 10

MISRA-C (2)● Beispiel:

● Problem: Es können trotzdem diverse Fehler auftreten– Memory Leaks– Zugriffe über Array-Grenzen hinaus– …

➔ Nur der Stil wird verbessert, nicht die Sprache

Rule 34 (required)The operands of a logical && or || shall be primary expressions.

Invalid: if ( x == 0 && ishigh )Valid: if ( ( x == 0 ) && ishigh )

Primary expressions are constants, a single identifier such as ishigh, or a parenthesized expression. Parentheses are important for readability and ensuring that the behavior is what the programmer intends.

Rule 34 (required)The operands of a logical && or || shall be primary expressions.

Invalid: if ( x == 0 && ishigh )Valid: if ( ( x == 0 ) && ishigh )

Primary expressions are constants, a single identifier such as ishigh, or a parenthesized expression. Parentheses are important for readability and ensuring that the behavior is what the programmer intends.

Page 11: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 11

Embedded C++ (EC++)● Teilmenge von C++ für eingebettete Systeme

– Festgelegt seit 1996 von einem japanischen Industriekonsortium,u.a. Hitachi, NEC, Fujitsu, Toshiba

● Weglassen wurden …– kostenbehaftete C++-Sprachmerkmale

● RTTI● Exceptions

– komplexe Merkmale, die somit Fehlerquellen sind● Mehrfachvererbung (teils auch Kostengründe)● Templates● mutable-Qualifizierer

– Merkmale, die man einfach für unnötig hielt● Namespaces, New-style Casts

● Einige C++-Compiler erlauben Einschränkung auf EC++– oder auch nur EC++ (kein C++)

Page 12: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 12

Cyclone: Ein sicheres C [1]● C-Spracherweiterung, die undefiniertes Verhalten vermeiden hilft

– Ein erweitertes Typsystem und statische Analyse– Laufzeitabsicherungen

● Konkrete Maßnahmen– Spezielle sichere Zeigertypen (Fat, Thin und Bounded)– Sicherer Umgang mit NULL– Erzwungene Initialisierung– Sichere Unions– Region-based Type System– Verschiedene Strategien zur Heap-Verwaltung (inkl. Garbage Collection)– Exceptions, Namespaces, Subtyping, u.v.m.

Page 13: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 13

Cyclone: Beispiele (1)● Fat Pointers

– Enthalten implizit den erlaubten Adressbereich– Dereferenzierungen außerhalb des Bereich führen zu Exception

#include <stdio.h>int main(int argc, char *@fat *@fat argv) { argc--; argv++; /* skip command name */ while (argc > 0) { printf(" %s", *argv); argc--; argv++; } printf("\n"); return 0;}

Das Speicher-Layout der Arrays ist nicht anders als bei C. Die fetten Zeiger enthalten die Information über die Dimension. Zeigerarithmetik ist erlaubt, aber kontrolliert.

Das Speicher-Layout der Arrays ist nicht anders als bei C. Die fetten Zeiger enthalten die Information über die Dimension. Zeigerarithmetik ist erlaubt, aber kontrolliert.

Page 14: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 14

Cyclone: Beispiele (2)● Thin Pointers

– Verweisen auf ein einzelnes Objekt– Es finden keine Laufzeitüberprüfungen statt– Zeigerarithmetik ist aber komplett verboten

● Garantierte Initialisierung– Realisiert durch statische Kontrollflussanalyse

int x = 3;int *y = &x;

Form *f;switch (event->eType) {case frmOpenEvent: f = FrmGetActiveForm(); ...case ctlSelectEvent: i = FrmGetObjectIndex(f, field); ...}

Fehler! 'f' ist hiernicht initialisiert.Fehler! 'f' ist hiernicht initialisiert.

Page 15: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 15

Cyclone: Beispiele (3)● Tagged Unions

– Optionale Erweiterung von Unions– Das Union-Objekt merkt sich seinen aktuellen Typ– Lesezugriffe werden kontrolliert– Der Typ kann auch explizit abgefragt werden

@tagged union U { int i; int *p; };void pr(union U x) { if (tagcheck(x.i)) printf("int(%d)", x.i); else printf("ptr(%d)", *x.p);}

Page 16: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 16

Cyclone: Kosten● Die Sicherheit hat ihren Preis

– Im Vergleich zu anderen sicheren Sprachen ist es jedoch effizient

Page 17: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 17

Fazit: Sichere/bessere C/C++ Dialekte● MISRA C und Embedded C++ reduzieren die

Fehlerwahrscheinlichkeit durch die Beschränkung auf eine Teilmenge von C bzw. C++.– Der Compiler oder separate Werkzeuge können die Einhaltung der

Regeln zur Übersetzungszeit prüfen– Trotzdem bleiben viele zentrale Probleme bestehen

● Unsichere Zeiger, unkontrollierte Arrays, manuelle Heap-Verwaltung, …● Cyclone geht deutlich weiter

– Sichere Zeiger, Unions, Heap-Verwaltung, …– Cyclone-Programme sind typsicher und laufen ohne undefiniertes

Verhalten durch.

Page 18: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 18

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

Page 19: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 19

Was macht Objektorientierung aus?

● wesentliches Alleinstellungsmerkmal: Vererbung● im Fall von C++ sind zu untersuchen:

– Einfach/Mehrfach-Vererbung– virtuelles Vererben– dynamisches Binden

Klassifizierung nach P. Wegner [3]:

object-oriented = data abstraction+ abstract data types+ type inheritance

Page 20: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 20

(Einfach-)Vererbung● eine abgeleitete Klasse erbt von einer Basisklasse

– geerbt werden Attribute, Methoden, ...● statt einer Instanz der Basisklasse kann

immer auch eine Instanz der abgeleitetenKlasse verwendet werden– gilt nicht umgekehrt!– möglichst kompatibles Objekt-Layout– Liskov'sches Substitutionsprinzip [4]

● Methoden können hinzugefügt oderüberdefiniert werden

Baseb1: intb2: int

Derivedd: intf: void ()

Page 21: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 21

(Einfach-)Vererbung – Ressourcen (1)

● die Attribute der Basisklasse liegen im Speicher am Anfang des Objekts

● keine Zeigeranpassung bei Typumwandlung Base* → Derived* oder Derived* → Base* nötig

struct Base { int b1, b2;};

struct Derived : Base { int d; void f ();};

struct Base { int b1, b2;};

struct Derived : Base { int d; void f ();};

C++-Code der Klassen Objekt-Layout [5]

Base-Instanz

Derived-Instanz

this this

int b1int b2

int d

int b1int b2

Page 22: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 22

(Einfach-)Vererbung – Ressourcen (2)

● Methoden erhalten den this-Pointerals unsichtbaren ersten Parameter

● Zugriff auf eigene Attribute und Basisklassenattribute kosten gleich viel.

● Kein Overhead durch (Einfach-)Vererbung.

Derived derived;

int main () { derived.f ();}

void Derived::f () { b1 = 1; b2 = 2; d = 3;}

Derived derived;

int main () { derived.f ();}

void Derived::f () { b1 = 1; b2 = 2; d = 3;}

.bssderived: .zero 12

.bssderived: .zero 12

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl $1, (%eax) movl $2, 4(%eax) movl $3, 8(%eax) popl %ebp ret

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl $1, (%eax) movl $2, 4(%eax) movl $3, 8(%eax) popl %ebp ret

main: pushl %ebp movl %esp, %ebp pushl $derived call _ZN7Derived1fEv xorl %eax, %eax leave ret

main: pushl %ebp movl %esp, %ebp pushl $derived call _ZN7Derived1fEv xorl %eax, %eax leave ret

Page 23: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 23

Mehrfachvererbung

● eine abgeleitete Klasse erbt von mehreren Basisklassen– eine auf dem „Einfachvererbungspfad“– N-1 auf dem „Mehrfachvererbungspfad“

● Vererbungshierarchie ist keine Baumstruktur mehr● mehrfaches Erben von derselben Klasse möglich!

Multim: intfm: int ()

Singles: intfs: int ()

Derivedf: void ()

Page 24: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 24

Mehrfachvererbung – Ressourcen (1)

● die Attribute der Basisklassen liegen nacheinander im Speicher am Anfang des Objekts

● bei der Typumwandlung von Derived* in einen Zeiger auf eine Klasse im Mehrfachvererbungspfad muss ein Offset addiert werden

struct Single { int s; int fs();};

struct Multi { int m; int fm();};

struct Derived : Single, Multi /*, ...*/ { int f ();};

struct Single { int s; int fs();};

struct Multi { int m; int fm();};

struct Derived : Single, Multi /*, ...*/ { int f ();};

C++-Code der KlassenObjekt-Layout [3]

Derived-Instanz

this (Derived)this (Single)

this (Multi)

∆S

......

int s

int m

Page 25: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 25

Mehrfachvererbung – Ressourcen (2)

● Beim Aufruf einer Methodeder Klasse im Einfach-vererbungspfad kann derthis-Pointer einfachdurchgereicht werden

● Beim Zugriff auf Multi muss this angepasst werden (+ 4)● Bei inline-Methoden tritt das Problem nicht auf● Geringer Overhead bei Mehrfachvererbung

void Derived::f () { fs (); fm ();}

void Derived::f () { fs (); fm ();}

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp pushl %ebx movl 8(%ebp), %ebx pushl %ebx call _ZN6Single2fsEv addl $4, %ebx pushl %ebx call _ZN5Multi2fmEv popl %eax movl -4(%ebp), %ebx popl %edx leave ret

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp pushl %ebx movl 8(%ebp), %ebx pushl %ebx call _ZN6Single2fsEv addl $4, %ebx pushl %ebx call _ZN5Multi2fmEv popl %eax movl -4(%ebp), %ebx popl %edx leave ret

Page 26: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 26

Virtuelle Vererbung● durch virtuelle Vererbung wird

vermieden, dass eine mehrfachgeerbte Basis mehr als einmalinstanziiert wird.

● Speicherplatz im Objekt wirdeingespart

● Mehrdeutigkeiten bei der Namens-auflösung werden vermieden

● Wo werden die Instanzen der virtuellen Basisklasse Common abgelegt?

Derivedf: void ()

Leftl: int

Rightr: int

Commonc: int

Page 27: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 27

Virtuelle Vererbung – Ressourcen (1)

● die Attribute virtueller Basisklassen liegen am Ende● der Objekttyp-spezifische Offset macht die Typkonvertierung

kompliziert– (mindestens) eine virtuelle Funktionstabelle wird benötigt!

struct Common { int c; };

struct Left : virtual Common { int l;};

struct Right : virtual Common { int r;};

struct Derived : Left, Right { int d; void f ();};

struct Common { int c; };

struct Left : virtual Common { int l;};

struct Right : virtual Common { int r;};

struct Derived : Left, Right { int d; void f ();};

C++-Code der Klassen Objekt-Layout [3]

Derived-Instanz

this (Derived)this (Left)

this (Common)

∆CDerived

VtableDerived

VtableDerived-

Rightint d

int l

int r

∆CDerived

∆CRightint c

Page 28: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 28

Virtuelle Vererbung – Ressourcen (2)

● Der Zugriff auf ein Attributeiner virtuellen Basisklasseist erheblich komplizierter

● Dazu kommen noch (in diesem Beispiel):– 90 Byte für Tabellen– Konstruktor-Code zum Initialisieren der Vtable-Zeiger

● Deutlicher Overhead bei virtueller Vererbung!– insbesondere, wenn die beteiligten Klassen sonst keine Vtable

benötigen würden

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx movl -12(%edx), %edx movl $1, (%edx,%eax) movl $2, 4(%eax) movl $3, 12(%eax) movl $4, 16(%eax) popl %ebp ret

_ZN7Derived1fEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx movl -12(%edx), %edx movl $1, (%edx,%eax) movl $2, 4(%eax) movl $3, 12(%eax) movl $4, 16(%eax) popl %ebp ret

void Derived::f () { c = 1; l = 2; r = 3; d = 4;}

void Derived::f () { c = 1; l = 2; r = 3; d = 4;}

Page 29: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 29

Dynamisches Binden● dynamisches Binden

erfolgt bei virtuellenFunktionen– C++-Schlüsselwort virtual

● die Zielfunktion einesAufrufs wird dabei zurLaufzeit ermittelt– wäre bv() nicht virtuell, würde in den Beispielen immer Base::bv()

ausgeführt werden

– ob Base::bv() oder Derived::bv() ausgeführt wird, hängt vom Objekttyp (nicht vom Zeigertyp) ab

– da Base::b() sowohl auf Base- als auch Derived-Objekten ausgeführt werden kann, muss der Objekttyp ermittelt werden

– da nicht immer zur Übersetzungszeit bestimmt werden kann, worauf p in main() zeigt, muss auch hier der Typ ermittelt werden

Baseb: void ()bv: void ()

Derivedbv: void ()

void Base::b () { bv ();} ?

int main () { Base *p = ...; p->bv ();} ?

Page 30: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 30

Dynamisches Binden – Ressourcen (1)

● die klassenspezifischen virtuellen Funktions-tabellen enthalten Zeiger auf den passenden Code

● der Konstruktor muss den Vtable-Zeiger eintragen!– ggf. sogar mehrfach überschreiben!

struct Base { void b (); virtual void bv () {}};

struct Derived : Base { void bv () {} // virtuell};

struct Base { void b (); virtual void bv () {}};

struct Derived : Base { void bv () {} // virtuell};

C++-Code der KlassenObjekt-Layout [3]

Derived-Instanz

this (Derived)this (Base)

VtableDerived

VtableBase

Base-Instanz

this (Base)

Base::bv()

Derived::bv()

Page 31: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 31

Dynamisches Binden – Ressourcen (2)

● Virtuelle Funktionsaufrufe wie inBase::b() und main() bedingen eine Indirektion– kein Inlining solcher Aufrufe möglich!– selbst leere virtuelle Funktionen müssen angelegt werden

● Dynamisches Binden kostet deutlich mehr als statisches!

main: pushl %ebp movl %esp, %ebp pushl $4 call _Znwj movl $_ZTV7Derived+8, (%eax) movl %eax, (%esp) call *_ZTV7Derived+8 xorl %eax, %eax leave ret

main: pushl %ebp movl %esp, %ebp pushl $4 call _Znwj movl $_ZTV7Derived+8, (%eax) movl %eax, (%esp) call *_ZTV7Derived+8 xorl %eax, %eax leave ret

int main () { Base *p = new Derived; p->bv();}

int main () { Base *p = new Derived; p->bv();}

void Base::b () { bv ();}

void Base::b () { bv ();}

_ZN4Base2bvEv: pushl %ebp movl %esp, %ebp popl %ebp ret

_ZN4Base2bvEv: pushl %ebp movl %esp, %ebp popl %ebp ret

_ZN4Base1bEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx pushl %eax call *(%edx) popl %eax leave ret

_ZN4Base1bEv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx pushl %eax call *(%edx) popl %eax leave ret

Page 32: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 32

Kosten von Objektorientierung in C++● Einfachvererbung

– praktisch keine zusätzlichen Kosten● Mehrfachvererbung

– Zeigerkonvertierung Mehrfachvererbungspfad, geringer Aufwand● Virtuelle Vererbung

– Vtable-Speicher, Objektinitialisierung, indirekter Zugriff, Aufwand!● Dynamisches Binden

– Vtable-Speicher, Objektinitialisierung, indirekter Aufruf, Aufwand!● Faustregel: In ressourcenbeschränkten Domänen das virtual-

Schlüsselwort nur verwenden, wenn es wirklich nötig ist

Page 33: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 33

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

Page 34: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 34

● Häufig werden dieselben Algorithmen für verschiedene Datentypen benötigt, z.B. quicksort() für int, float, u.s.w. oder Listen von int-, Foo- oder Bar-Objekten.

● Wie kann der Programmierer damit umgehen, z.B. in C oder Java < 5?– Mehrfachimplementierung des Algorithmus

● Probleme bei der Wartung, Wiederholung von Fehlern, Mühe!– Gemeinsame Basis

● fehlende Typsicherheit bzw. Typüberprüfung erst zur Laufzeit– Präprozessoren (z.B. C-Makros)

● blinde Textersetzung, Scopes und Typen werden ignoriert● Templates sind ein standardisierter [2] C++-Mechanismus,

der alle diese Probleme vermeidet!

Warum Templates?

Page 35: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 35

● als Template-Parameter kann im Prinzip jeder Typ verwendet werden (keine gemeinsame Basis nötig!)

● Einschränkungen definiert das Template implizit, hier:– T benötigt operator < (const T&) const

– die Aufrufparameter von max() müssen denselben Typ haben, damit T „deduziert“ werden kann

● int i = max(2, 3); // OK● int k = max(4, 4.2); // Fehler

Ein erstes Funktions-Templatemax.hmax.h

template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}

template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}

Template-Parameter (altern. <class T>)Template-Parameter (altern. <class T>)

T kann wie ein normaler Typverwendet werdenT kann wie ein normaler Typverwendet werden

Page 36: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 36

Überladen von Funktions-Templatesmax.hmax.h

template <typename T>inline const T& max (const T &a, const T &b) {...}

template <typename T>inline const T& max (const T &a, const T &b, const T &c) {...}

inline const int& max (const int &a, const int &b) {...}

template <typename T>inline const T& max (const T &a, const T &b) {...}

template <typename T>inline const T& max (const T &a, const T &b, const T &c) {...}

inline const int& max (const int &a, const int &b) {...}

main.ccmain.cc

#include "max.h"int main() { max(1,2,3); // Template mit 3 Argumenten max(1.0,2.0); // max<double> per Deduktion max('X','Y'); // max<char> per Deduktion max(1,2); // nicht-Template Variante bevorzugt max<>(1,2); // max<int> per Deduktion max<double>(1,2); // max<double> ohne Deduktion max('X',3.14); // nicht-Template Variante für 2 ints}

#include "max.h"int main() { max(1,2,3); // Template mit 3 Argumenten max(1.0,2.0); // max<double> per Deduktion max('X','Y'); // max<char> per Deduktion max(1,2); // nicht-Template Variante bevorzugt max<>(1,2); // max<int> per Deduktion max<double>(1,2); // max<double> ohne Deduktion max('X',3.14); // nicht-Template Variante für 2 ints}

Page 37: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 37

● Klassen-Templates sind perfekt für Container-Klassen– darum gibt es auch die Standard Template Library (STL)

Ein erstes Klassen-TemplateVector.hVector.h

template <typename T>class Vector { T *data; int dim;public: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};

template <typename T>class Vector { T *data; int dim;public: // Konstruktor, Copy-Konstruktor(!), ... // Zugriffsfunktionen z.B. mit Indexprüfung: void set (int index, const T& obj); T get (int index) const;};

main.ccmain.cc

#include "Vector.h"int main() { Vector<float> a_vector(3); a_vector.set (0, 2.71);}

#include "Vector.h"int main() { Vector<float> a_vector(3); a_vector.set (0, 2.71);}

Page 38: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 38

● Wenn Methoden von Klassen-Templates nicht im Klassenrumpf definiert werden, müssen sie ähnlich wie ein Funktions-Template formuliert werden:

Methoden von Klassen-Templates

vector.hvector.h

#include <assert.h>template <typename T>void Vector<T>::set (int index, const T& obj) { assert (index < dim); // wird nur in der Debug-Variante // geprüft data[index] = obj; // erfordert operator = in T}

template <typename T>T Vector<T>::get (int index) { assert (index < dim); // wird nur in der Debug-Variante // geprüft return data[index]; // erfordert Copy-Konstruktor}

#include <assert.h>template <typename T>void Vector<T>::set (int index, const T& obj) { assert (index < dim); // wird nur in der Debug-Variante // geprüft data[index] = obj; // erfordert operator = in T}

template <typename T>T Vector<T>::get (int index) { assert (index < dim); // wird nur in der Debug-Variante // geprüft return data[index]; // erfordert Copy-Konstruktor}

Page 39: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 39

Ressourcenverbrauch von Templates● Bei Inline-Funktionen effizient wie Makros, aber typsicher

● Gefahr des Code Bloat– Auswirkung auf die Codegröße und weniger auf die Performance

testmax.cctestmax.cc

template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}

int f() { return max (3, max (1, 2)); }

template <typename T>inline const T& max (const T &a, const T &b) { return a < b ? b : a;}

int f() { return max (3, max (1, 2)); }

_Z1fv: push %ebp mov $0x3,%eax mov %esp,%ebp pop %ebp ret

_Z1fv: push %ebp mov $0x3,%eax mov %esp,%ebp pop %ebp ret

<int const& max<int>(int const&, int const &)>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 4d 08 mov 0x8(%ebp),%ecx 6: 8b 55 0c mov 0xc(%ebp),%edx 9: 8b 01 mov (%ecx),%eax b: 3b 02 cmp (%edx),%eax d: 7c 02 jl 11 f: 89 ca mov %ecx,%edx 11:89 d0 mov %edx,%eax 13:5d pop %ebp 14:c3 ret

<int const& max<int>(int const&, int const &)>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 4d 08 mov 0x8(%ebp),%ecx 6: 8b 55 0c mov 0xc(%ebp),%edx 9: 8b 01 mov (%ecx),%eax b: 3b 02 cmp (%edx),%eax d: 7c 02 jl 11 f: 89 ca mov %ecx,%edx 11:89 d0 mov %edx,%eax 13:5d pop %ebp 14:c3 ret

<long const& max<long>(long const&, long const &)>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 4d 08 mov 0x8(%ebp),%ecx 6: 8b 55 0c mov 0xc(%ebp),%edx 9: 8b 01 mov (%ecx),%eax b: 3b 02 cmp (%edx),%eax d: 7c 02 jl 11 f: 89 ca mov %ecx,%edx 11:89 d0 mov %edx,%eax 13:5d pop %ebp 14:c3 ret

<long const& max<long>(long const&, long const &)>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 4d 08 mov 0x8(%ebp),%ecx 6: 8b 55 0c mov 0xc(%ebp),%edx 9: 8b 01 mov (%ecx),%eax b: 3b 02 cmp (%edx),%eax d: 7c 02 jl 11 f: 89 ca mov %ecx,%edx 11:89 d0 mov %edx,%eax 13:5d pop %ebp 14:c3 ret

identisch!

Page 40: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 40

Inhalt● Motivation● Sicheres Programmieren in C und C++

– MISRA C– Embedded C++– Cyclone

● Ressourcenverbrauch von C++ gegenüber C– Laufzeit-Polymorphie– Parametrische Polymorphie

● Zusammenfassung

Page 41: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 41

Zusammenfassung● C und C++ sind nicht typsicher

– Quelle für diverse Fehler und Sicherheitslücken– Teilmengen wie MISRA C und Embedded C++ helfen nur bedingt

● Dafür bieten C und C++ …– (Kosten-)Transparenz

● Man „sieht“ wofür die Ressourcen verbraucht werden● Das Systemverhalten ist vorhersagbar (z.B. kein Garbage Collector)

– Kontrolle über den Ressourcenverbrauch● Man kann den Ressourcenverbrauch beeinflussen (tuning)

… und sind daher dominierend in eingebetteten Systemen● C++ erweitert C primär um zwei neue Paradigmen

– Objektorientierung● Vorsicht Kostenfalle

– Generische Programmierung● Komplex, aber keine Laufzeitkosten → geeignet für eingebettete Produktlinien

Page 42: SuS-06.1: Anwendungsentwicklung in C/C++ · 2021. 1. 29. · – C-Datentypen werden direkt auf die Hardwaredatentypen abgebildet In Java wird von der realen Hardware abstrahiert

03.07.2018 SuS: 06.1 Anwendungsentwicklung in C/C++ 42

Literatur[1] T. Jim, J. G. Morrisett, D. Grossman, M. W. Hicks, J. Cheney, and Y.

Wang. Cyclone: A Safe Dialect of C. In Proceedings of the General Track of the Annual Conference on USENIX Annual Technical Conference (C. S. Ellis, Ed.). USENIX Association, Berkeley, CA, pages 275-288, June 2002.

[2] Guidelines for the Use of the C Language in Critical Systems, ISBN 0 9524156 2 3 (paperback), ISBN 0 9524156 4 X (PDF), October 2004.

[3] P. Wegner. Classification in Object-Oriented Systems, ACM, SIGPLAN Notices, 21(10):173-182, 1986.

[4] B. Liskov. Data Abstraction and Hierarchy, ACM, SIGPLAN Notices, 23(5), 1988.

[5] C++ ABI Summary, http://www.codesourcery.com/cxx-abi