diff --git a/index.html b/index.html new file mode 100644 index 0000000..0b34024 --- /dev/null +++ b/index.html @@ -0,0 +1,287 @@ + + + + + + + + Vorlesungsunterlagen Softwareprojekt + + + + +
+

Vorlesungsunterlagen Softwareprojekt

+

Martin Sulzmann

+
+

Inhalt

+

Übersicht

+ +

Unterlagen auf Deutsch und Englisch als html mit komplettem +Programmcode.

+

Kurz und knapp

+ +

Einführung in C

+

Teil 1

+ +

Teil 2

+ +

Teil 3

+ +

Einführung in C++

+

Teil 1

+

C++ kompakt

+ +

Teil 2

+

Nochmal Zeiger und Resourcen Kontrolle.

+ +

Teil 3

+

Polymorphie

+

Teil 4

+

Funktionaler Programmierstil

+

Teil 5

+

QuickCheck: Automatisiertes Testen

+

Laboraufgaben und Beispiele

+

Aufgabensammlung

+

Weitere Beispiele

+

Scheinaufgaben

+

Übung (Schein)

+

Weitere Themen

+ +

Software Entwicklung

+ +

Referenzen/Literatur

+ +

C++ online tutorials

+ +

C++ reference

+ + + diff --git a/lec-c-compact.html b/lec-c-compact.html new file mode 100644 index 0000000..f314896 --- /dev/null +++ b/lec-c-compact.html @@ -0,0 +1,1012 @@ + + + + + + + + + Die Programmiersprache C - Kurz und knapp + + + + + +
+

Die Programmiersprache C - Kurz und knapp

+

+Martin Sulzmann +

+
+
+

Übersicht

+ +
+
+

Grundlagen

+ +
#include <stdio.h>
+
+/*
+
+- Vorwärtsdeklarationen
+- Ausgabe via printf
+- Referenztypen (aka Zeiger/Pointer)
+- Call-by value versus call-by reference
+- Eingabe via scanf
+- Strukturen
+
+*/
+
+
+//////////////////////////////////////////////////
+// Vorwärtsdeklarationen
+
+
+// Wir rufen "printSomething" auf.
+// 1. Die Definition von "printSomething" ist (textuell) erst danach.
+// 2. Wir muessen aber "printSomething" vorher deklaration.
+// 3. So wissen wir, dass es "printSomething" gibt.
+
+void printSomething();
+
+void printHallo() {
+  printSomething();
+  printf("\nHallo");
+}
+
+void printSomething() {
+  printf("\nSomething");
+}
+
+
+//////////////////////////////////////////////////
+// Ausgabe via printf
+
+void printEx() {
+  int i = 5;
+  float f = 3.2;
+  char c = 'a';
+
+  printf("\n i = %d", i);
+  printf("\n f = %f", f);
+  printf("\n c = %c", c);
+
+  printf("\n i = %d, f = %f, c = %c", i, f, c);
+
+
+ // Typinformation kodiert im Formatstring
+ // Z.B. Platzhalter %f steht fuer eine float Zahl.
+ // printf ist eine "variadic" function, nimmt einen Variable Anzahl von Argumenten.
+
+ // Beachte:
+ // Typen der Argumente muessen mit Platzhaltern kompatibel sein.
+
+}
+
+
+//////////////////////////////////////////////////
+// Referenztypen (aka Zeiger/Pointer)
+
+
+// 1. "int x",     x ist eine Variable in welcher wir Dezimalwerte speichern koennen
+// 2. "int* p",    p ist eine Variable in welcher wir Referenzen auf Dezimalwerte speichern koennen.
+//
+//                 Sprachgebrauch in C:
+//                     p ist ein Zeiger/Pointer
+//
+//                 Operationen:
+//                     & ist der Referenz/Adress-Operator
+//                     * ist der Dereferenzierungs-Operator
+
+void references() {
+  int x;
+  float y;
+  int* p;
+  float *q;
+
+  p = &x;              // In p findet sich die Referenz auf x.
+  *p = 1;              // Indirekter Zugriff auf x.
+  printf("%d", x);
+
+
+  /*
+  // warning: Inkompatibler Zeigertyp
+  q = &x;
+  */
+
+  // Vorsicht!!!
+  // 1. int Zeiger wird auf float Zeiger gecastet.
+  // 2. Die Referenz wird als float interpretiert.
+  // 3. Kann zum Absturz fuehren.
+  y = * ((float*)(p));
+
+}
+
+//////////////////////////////////////////////////
+// Call-by value versus call-by reference
+
+int inc(int x) {
+  x = x + 1;
+  return x;
+}
+
+// Call-by-reference via Zeiger.
+void incRef(int* x) {
+  *x = *x + 1;
+  return;
+}
+
+void cbvCBR() {
+  int x = 1;
+
+  printf("\n %d", inc(x));
+  printf("\n %d", x);
+
+  incRef(&x);
+  printf("\n %d", x);
+
+
+}
+
+
+//////////////////////////////////////////////////
+// Eingabe via scanf
+
+
+// scanf verwendet call-by-reference
+void scanEx() {
+  int i;
+  char c;
+
+  printf("\nEingabe von Zeichen:");
+  scanf("%c",&c);
+  printf("\nEingabe von Zahl:");
+  scanf("%d",&i);
+
+  printf("%d %c",i,c);
+
+}
+
+
+//////////////////////////////////////////////////
+// Strukturen
+
+struct Punkt {
+  int x;
+  int y;
+};
+
+void printPunkt(struct Punkt p) {
+  printf("\n Punkt = (%d,%d)", p.x, p.y);
+}
+
+
+struct Punkt scanPunkt() {
+  struct Punkt p;
+
+  printf("\n Eingabe x:");
+  scanf("%d", &p.x);         // &p.x entspricht &(p.x)
+
+  printf("\n Eingabe y:");
+  scanf("%d", &p.y);
+
+  return p;
+}
+
+// In C gilt call-by value.
+// Deshalb ist die Eingabe nicht von aussen sichtbar.
+void scanPunkt2(struct Punkt p) {
+  struct Punkt r;
+  r = scanPunkt();
+  p = r; // Bedeutet, p.x = r.x; p.y = r.y;
+}
+
+// Call-by-reference via Zeiger ("pointer").
+void scanPunktRef(struct Punkt* p) {
+  struct Punkt r;
+
+  r = scanPunkt();
+
+  (*p).x = r.x;  // Beachte: *p.x bedeutet Zugriff auf x und danach Dereferenzierung.
+  (*p).y = r.y;
+}
+
+void punkt() {
+  struct Punkt p;
+  struct Punkt q;
+  struct Punkt r;
+
+  p = scanPunkt();
+  printPunkt(p);
+
+  scanPunkt2(q);
+  printPunkt(q);
+
+  scanPunktRef(&r);     // Uebergebe Referenz auf r
+  printPunkt(r);
+}
+
+//////////////////////////////////////////////////
+// main, wird immer am Anfang aufgerufen
+
+int main() {
+  printHallo();
+  printEx();
+  references();
+  cbvCBR();
+  scanEx();
+  punkt();
+}
+
+
+

Zeigerarithmetik

+ +

+#include <stdio.h>
+
+/*
+
+- Arrays
+- Zeigerarithmetik
+- Strings
+- Funktionszeiger
+
+*/
+
+
+/////////////////////////////////////////
+// Arrays
+
+// Array
+// 1. Elemente sind alle vom gleichen Typ
+// 2. Fixe Länge
+// 3. Kein "out-of-bounds" check!
+
+void arrays() {
+  int i;
+  int a[5] = { 1, 2, 3, 4, 5};
+
+  for(i=0; i < 6; i++) {
+    printf("%d", a[i]);
+  }
+
+
+}
+
+/////////////////////////////////////////
+// Zeigerarithmetik
+
+// 1. Arrayname ist eine Referenz auf das erste Element
+// 2. p sei ein Zeiger dann ist
+//     p + 1   die Referenz auf den Nachfolger und
+//     p - 1   die Referenz auf den Vorgänger
+
+void zeiger() {
+  int i;
+  int a[5] = { 1, 2, 3, 4, 5};
+  int* p;
+
+  p = a;
+  // entspricht
+  // p = &a[0];
+
+  printf("%d", *p);  // Ausgabe erstes Element
+  p = p + 1;         // Zeiger verweist auf zweites Element
+  printf("%d", *p);  // Ausgabe zweites Element
+  p = p + 2;
+  printf("%d", *p);  // Ausgabe viertes Element
+  p = p - 1;
+  printf("%d", *p);  // Ausgabe drittes Element
+
+}
+
+
+/////////////////////////////////////////
+// Strings
+
+// 1. Eine String ist ein Array von Zeichen
+// 2. In C wird folgender Trick angewandt um die Laenge des strings zu verwalten.
+// 3. Das Ende eines strings in C wird mit einem speziellen ASCII Code gekennzeichnet.
+// Der ASCII Code 0 ("Null") wird verwendet, um das Ende des strings zu markieren.
+// Dies wird auch als "Null Terminierung" bezeichnet und diesen
+// speziellen ASCII Code nennen wir auch Null Terminator.
+
+
+int len(char s[]) {
+  int i = 0;
+
+  while(s[i] != '\0') {
+    i++;
+  }
+  return i;
+}
+
+// Variante.
+// char s[] entspricht const char*s
+// Da wir den Zeiger s inkrementieren verwenden wir char* s.
+int len2(char* s) {
+  int i = 0;
+
+  while(*s != '\0') {
+    i++;
+    s++;
+  }
+  return i;
+}
+
+
+void strings() {
+
+  char str[] = "Hello";
+
+  char str2[] = "12";
+
+  // Falls elementweise Initialisierung muss explizit der Nullterminator am Schluss stehen!
+  char str3[] = { '1', '2', '\0'};
+
+  char str4[] = { '1', '2'};
+
+
+  printf("%d", len(str));
+  printf("%d", len2(str));
+
+  printf("%d", len(str3));
+  printf("%d", len(str4));
+
+}
+
+
+/////////////////////////////////////////
+// Funktionszeiger
+
+
+// Parameter f ist ein Zeiger (Referenz) auf eine Funktion.
+// Diese Funktion erwartet einen int Wert und liefert einen int Wert zurueck.
+void mapInt(int* p, int len, int (*f)(int)) {
+ int i;
+ for(i=0; i<len; i++)
+   p[i] = (*f)(p[i]);
+}
+
+// Beachte.
+// Oft werden "shorthands" via typedef verwendet.
+
+typedef int (*intFunc)(int);
+
+void mapInt2(int* p, int len, intFunc f) {
+ int i;
+ for(i=0; i<len; i++)
+   p[i] = (*f)(p[i]);
+}
+
+// In "modernem" C koennen wir schreiben.
+void mapIn3(int* p, int len, int f(int)) {
+ int i;
+ for(i=0; i<len; i++)
+   p[i] = (*f)(p[i]);
+}
+
+
+
+int inc(int x) { return x+1; }
+int square(int x) { return x*x; }
+
+
+void funktionsZeiger() {
+   int x[5] = { 1,2,3,4,5 };
+   mapInt(x,5,&inc);
+   mapInt(x,5,&square);
+
+   printf("%d", x[1]);
+}
+
+
+int main() {
+  arrays();
+  zeiger();
+  strings();
+  funktionsZeiger();
+}
+
+
+

Speicherverwaltung

+ +

+#include <stdio.h>
+#include <stdlib.h> // malloc, free
+
+/*
+
+Speicherverwaltung:
+
+- Stack versus Heap
+- malloc und free, zu viele versus zu wenige "deletes"
+
+*/
+
+//////////////////////////////////////////////////
+// Stack allokierter Speicher
+
+void stack() {
+  int i;
+  int* p;
+
+  i = 2;
+  p = &i;   // p verweist auf Stack-allokierten Speicherbereich
+            // Automatische Freigabe nach Ruecksprung aus dieser Funktion
+
+  printf("%d", *p);
+}
+
+// "warning: address of stack memory associated with local variable 'i' returned"
+int* stackMem() {
+  int i;
+
+  i = 2;
+  printf("%d",i);
+
+  return &i;
+}
+
+void stackInvalidAccess() {
+  int* p;
+
+  p = stackMem();
+  printf("%d", *p);
+
+}
+
+
+//////////////////////////////////////////////////
+// Heap allokierter Speicher
+
+void heapFehlendesDelete() {
+  int* p;
+
+  p = (int*)malloc(sizeof(int));   // Heap Allokation
+  //               ^^^^^^^^^^^
+  //               Anzahl der Bytes
+  //  ^^^^^
+  //  Zeiger auf int
+
+  *p = 2;
+
+  printf("%d", *p);
+
+  // Speicherleck.
+  // Heap allokierte Objekte muessen explizit freigegeben werden (manuelle Speicherverwaltung)
+}
+
+
+void heap() {
+  int* p;
+
+  p = (int*)malloc(sizeof(int));   // Heap Allokation
+
+  *p = 2;
+
+  printf("%d", *p);
+
+  free(p);                         // Speicherfreigabe
+}
+
+
+void heapDoppeltesDelete() {
+  int* p;
+
+  p = (int*)malloc(sizeof(int));   // Heap Allokation
+
+  *p = 2;
+
+  printf("%d", *p);
+
+  free(p);                         // Speicherfreigabe
+  free(p);                         // Speicherfreigabe
+}
+
+
+void heapZufruehesDelete() {
+  int* p;
+
+  p = (int*)malloc(sizeof(int));   // Heap Allokation
+
+  *p = 2;
+
+  free(p);                         // Speicherfreigabe
+
+  printf("%d", *p);
+
+}
+
+int main() {
+  stack();
+  stackInvalidAccess();
+  heapFehlendesDelete();
+  heap();
+  // heapDoppeltesDelete(); // fuehrt zum Absturz
+  heapZufruehesDelete();    // kann zum Absturz fuehren
+
+
+}
+
+
+

Testverfahren

+ +
#include <stdio.h>
+#include <stdlib.h>
+
+
+// Punkt im 2-dimensionalen Koordinatensystem
+typedef struct { int x; int y; } point;
+
+
+
+// Gleichheit zwischen Punkten
+int eqPoint(point p, point q) {
+  return (p.x == q.x) && (p.y == q.y);
+}
+
+// Ausgabe eines Punktes
+void printPoint(point p) {
+  printf("\n (x = %d, y = %d)", p.x, p.y);
+}
+
+// Koordinaten vertauschen
+// Buggy !!!!!!!!!!!!
+point switchPoint(point p) {
+  p.x = p.y;
+  p.y = p.x;
+
+  return p;
+}
+
+//////////////////////
+// Tests
+
+// User Tests
+
+void testUser() {
+  point p = {2,2};
+
+  printPoint(p);
+  printPoint(switchPoint(p));
+}
+
+// Unit Tests
+
+typedef struct { point input; point expected; } TestCase;
+
+void testUnit() {
+  int i;
+
+  TestCase tests[] = {
+    {{1,1}, {1,1}},
+    {{2,2}, {2,2}}
+  };
+
+
+  for(i=0; i<2; i++) {
+    if(eqPoint(switchPoint(tests[i].input),
+           tests[i].expected)) {
+      printf("\nOK");
+    } else {
+      printf("\nFAIL");
+    }
+  }
+}
+
+// Invarianten = Properties = Eigenschaften
+
+
+int property1(point p) {
+  return eqPoint(switchPoint(switchPoint(p)),
+         p);
+}
+
+void testInvariante() {
+  int i;
+  point ps[10];
+
+  // Generiere 10 Punkte zufaellig.
+  for(i=0; i<10; i++) {
+    ps[i].x = rand();
+    ps[i].y = rand();
+  }
+
+  for(i=0; i<10; i++) {
+    if(property1(ps[i])) {
+      printf("\nOK");
+    } else {
+      printf("\nFAIL");
+    }
+  }
+
+}
+
+int main() {
+  testUser();
+  testUnit();
+  testInvariante();
+}
+
+
+

Generische Datentypen und Funktionen in C

+ +
// Generische Datentypen und Funktionen in C.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+enum Type {
+  INT = 0,
+  FLOAT = 1
+};
+
+// Generische Variante
+// void* ist der generischer Zeiger in C.
+// Siehe "Object" in Java.
+// void* ist wie ein Zeiger ohne Typinformation.
+struct GenArray {
+  enum Type type;
+  void* start;
+  int len;
+};
+
+int sizeOf(enum Type type) {
+  switch(type) {
+  case INT: return sizeof(int);
+  case FLOAT: return sizeof(float);
+  }
+}
+
+struct GenArray new_GenArray(int n, enum Type type) {
+  void* q = malloc(n * sizeOf(type));
+
+  struct GenArray x = {type, q, n};
+
+  return x;
+}
+
+
+// Dereferenzierung von void* nicht moeglich.
+// Wir verwenden die in dem enum kodierte Typinformation,
+// um auf int*, float*, etc. zu casten.
+void print_Type(void* elem, enum Type type) {
+  int* i;
+  float* f;
+
+  switch(type) {
+  case INT:
+    i = (int*)(elem);
+    printf("%d", *i);
+    break;
+  case FLOAT:
+    f = (float*)(elem);
+    printf("%f", *f);
+    break;
+  }
+}
+
+// Pointer Arithmetik fuer generischen Zeiger.
+// Die in dem enum kodierte Typinformation ist wiederum wichtig.
+void* inc_pointer_Type(void* p, enum Type type) {
+  int* i;
+  float* f;
+
+  switch(type) {
+  case INT:
+    i = (int*)(p);
+    i++;
+    p = i;
+    break;
+  case FLOAT:
+    f = (float*)(p);
+    f++;
+    p = f;
+    break;
+  }
+  return p;
+}
+
+void print_GenArray(struct GenArray x) {
+  int i=0;
+  void* p = x.start;
+
+  for(i=0; i<x.len; i++) {
+    print_Type(p, x.type);
+    p = inc_pointer_Type(p, x.type);
+  }
+}
+
+void update_GenArray(struct GenArray x, int pos, void* new_Val) {
+  int* i; int* i_Val;
+  float* f; float* f_Val;
+
+  // "array" out-of-bounds check
+  if(pos >=0 && pos < x.len) {
+    switch(x.type) {
+    case INT:
+      i = (int*)(x.start);
+      i_Val = (int*)(new_Val);
+      i[pos] = *i_Val;
+      break;
+    case FLOAT:
+      f = (float*)(x.start);
+      f_Val = (float*)(new_Val);
+      f[pos] = *f_Val;
+      break;
+    }
+  }
+
+}
+
+void test_GenArray() {
+  struct GenArray x = new_GenArray(2, INT);
+  struct GenArray y = new_GenArray(3, FLOAT);
+  int i = 5;
+  float f = 1.0;
+
+  update_GenArray(x,0,&i);
+  update_GenArray(x,1,&i);
+
+  update_GenArray(y,0,&f);
+  update_GenArray(y,1,&f);
+  update_GenArray(y,2,&f);
+
+  print_GenArray(x);
+
+  print_GenArray(y);
+}
+
+int main() {
+  printf("\n");
+  test_GenArray();
+}
+
+
+

OO in C selbst nachgebaut

+ +
#include <stdio.h>
+#include <stdlib.h>
+
+/*
+
+Geometric objects.
+
+Java pseudo-code.
+
+class Shape {
+ int area();
+}
+
+class Rectangle extends Shape {
+  int length;
+  int width;
+  int area() { return length * width; }
+}
+
+class Square extends Shape {
+  int length;
+  int area() { return length * length; }
+}
+
+// Annahme, frei-stehende Funktionen in Java moeglich.
+int sumArea (Shape s1, Shape s2) {
+  return s1.area() + s2.area();
+}
+
+ */
+
+// Geometric objects in C
+
+typedef struct {
+  int length;
+  int width;
+} rectangle;
+
+typedef struct {
+  int length;
+} square;
+
+
+int areaRectangle(rectangle* r) {
+  return r->length * r->width;
+}
+
+int areaSquare(square* s) {
+  return s->length * s->length;
+}
+
+
+// Generic representation
+
+typedef struct {
+  void* obj;
+  int (*area)(void*); // Function pointer
+} shape;
+
+
+// Generic function
+int sumArea(shape s1, shape s2) {
+  return s1.area(s1.obj) + s2.area(s2.obj);
+}
+
+// Generic functionc (same as above)
+int sumArea2(shape s1, shape s2) {
+  return (*(s1.area))(s1.obj) + s2.area(s2.obj);
+}
+
+// Points to note.
+// In modern C, there's no need to dereference a function pointer.
+// "." binds tighter than "*".
+// Hence, the expression
+//     (*(s1.area))(s1.obj)
+// is equivalent to
+//     s1.area(s1.obj)
+
+// Some examples
+void testShape() {
+  rectangle r = {1,2};
+  square s = {3};
+  shape s1 = {&r, (int (*)(void *))(&areaRectangle)};
+  shape s2 = {&s, (int (*)(void *))(&areaSquare)};
+  shape s3 = {&s, (int (*)(void *))(&areaRectangle)};   // (P)
+  shape s4 = {&r, (int (*)(void *))(&areaSquare)};      // (Q)
+
+  printf("\n %d", sumArea(s1,s2));
+
+  printf("\n %d", sumArea(s3,s4));
+}
+
+/*
+
+Points to note.
+
+We emulate here "OO" in C. The user needs to precisely follow some coding guidelines.
+
+Consider (P) and (Q).
+The "object" (structure) does not match the "method" (function).
+The coding guidelines for emulating OO in C are broken.
+This may lead to some run-time errors.
+
+*/
+
+
+int main() {
+  testShape();
+}
+
+ +