Link zu Probeklausur


Aufgabe 1

a)

  • deklarieren Sie zuerst eventuelle benutzerdefinierte Typen:
struct haus {
	int zimmer;
	char* art;
	char gebaeude;
};
typedef struct haus haus;
  • definieren Sie dann die fünf Variablen ohne Initialisierung:
int a;
int* b;
 
double c[2];
 
haus d;
haus* e;
  • weisen Sie zum Schluss allen Variablen die in der Grafik gezeigten Werte zu:
#include <stdlib>
...
...
...
 
a = 1;
b = &a;
 
c[0] = 2.3;
c[1] = 4.5;
 
//d = {109, "Hoersaal", 'C'};
d.zimmer = 109;
d.art = "Hoersaal";
d.gebaeude = 'C';
 
e = (haus*) malloc(sizeof(haus));
e->zimmer = 124;
e->art = "Buero";
e->gebaeude = 'F';

Unterschied zwischen stack und heap

C Language Data Type Models: LP64 and ILP32

C TypeILP32LP64
char11
short22
int44
long48
long long88
pointer48

b)

Ohne Ausrichtung/Padding

ILP32:

Zimmer: (int) 4 Bytes
Art: (pointer) 4 Bytes
Gebaeude: (char) 1 byte

Also: Bytes

LP64:

Zimmer: (int) 4 Bytes
Art: (pointer) 8 Bytes
Gebaeude: (char) 1 Byte

Also: Bytes

Mit Ausrichtung/Padding

ILP32:

Zimmer: (int) 4 Bytes
Art: (pointer) 4 Bytes
Gebaeude: (char) 1 Byte
Padding: 3 Bytes

Also: Bytes

LP64:

Zimmer: (int) 4 Bytes
Padding nach Zimmer: 4 Bytes
Art: (pointer) 8 Bytes
Gebaeude: (char) 1 Byte
Padding nach Gebaeude: 7 Bytes

Also: Bytes


Aufgabe 2

EingabeparameterAusgabeparameter
ErklärungEingabeparameter sind Werte, die einer Funktion beim Aufruf übergeben werden.Ausgabeparameter sind Parameter, die verwendet werden, um Ergebnisse oder Ausgaben aus einer Funktion herauszugeben. Dies geschieht typischerweise durch die Übergabe von Zeigern oder Referenzen, die von der Funktion modifiziert werden können.
Beispielint add(int zahl){ return zahl + zahl; }void add(int a, int b, int* res) { *res = a + b;}

Aufgabe 3

a)

Wichtig

  • In header ifndef, define und endif nicht vergessen
  • In header files nur öffentliche Funktionen und Variablen.
  • Funktionennamen mit Klassennamen definieren
  • Eingabeparameter immer const
  • In Quelldatei (.c) include header nicht vergessen
  • In Quelldateien befinden sich private Funktionen
  • static heißt privat, also static in c ≠ static in java
quadrat.h
#ifndef QUADRAT_H
#define QUADRAT_H
 
double quadrat_flaeche(const double);
 
#endif
quadrat.c
#include "quadrat.h"
 
static double zumquadrat(const double d) 
{
	return d * d;
}
 
double quadrat_flaeche(const double seitenlaenge) 
{
	return zumquadrat(seitenlaenge);
}
wuerfel.h
#ifndef WUERFEL_H
#define WUERFEL_H
 
double wuerfel_oberflaeche(const double);
double wuerfel_volumen(const double);
 
#endif
wuerfel.c
#include "wuerfel.h"
#include "quadrat.h"
 
double wuerfel_oberflaeche(const double kantenlaenge) 
{
	return quadrat_flaeche(kantenlaenge) * 6;
}
 
double wuerfel_volumen(const double kantenlaenge) 
{
	return quadrat_flaeche(kantenlaenge) * kantenlaenge;
}

b)

wuerfel_test.c
#include <stdio.h>
#include "wuerfel.h"
 
int main(int argc, char *argv[]) 
{
	double k;
	if (sscanf(argv[1], "%lf", &k) == 0) 
	{
		printf("falsche eingabe!");
		return 1;
	}
	
	double f = wuerfel_oberflaeche(k);
	double v = wuerfel_volumen(k);
	printf("kantenlaenge %f, Oberflaeche %f, Volumen %f\n", k, f, v);
	
	return 0;
}

c)
Das C-Programm greift auf argv[1] zu, um das Argument zu lesen. Wenn das Programm jedoch ohne Argumente gestartet wird, ist argc gleich 1 und argv[1] existiert nicht. Der Zugriff auf argv[1] führt zu einem nicht definierten Verhalten, das in der Regel einen Absturz (Segmentation Fault) des Programms zur Folge hat.


Aufagabe 4

#include <string.h> // strcpy, strcat
#include <stdlib.h> // malloc, free
 
int main(int argc, char *argv[]) 
{	
	const char* s = argv[1];
	const char* t = argv[2];
	
	char* st = (char*) malloc(strlen(s) + strlen(t) + 1);
	strcpy(st, s);
	strcat(st, t);
	
	free(st);
	
	return 0;
}

Aufgabe 5

a)

~fuzzy() = default; // destructor
fuzzy(const fuzzy&) = default; // copy constructor
fuzzy(fuzzy&&) = default; // move constructor
fuzzy& operator=(const fuzzy&) = default; // copy assignment operator
fuzzy& operator=(fuzzy&&) = default; // move assignment operator

Grund für = default:

  • Da truth ein einfacher Datentyp (double) ist, funktioniert die Standardkopie und Verschieben korrekt.
  • Da truth keine komplexen Ressourcen wie dynamischen Speicher oder Datei-Handles enthält, ist der Standard-Destruktor ausreichend.

b)

int main () 
{
	const fuzzy eher_ja{ 0.8 };
	const fuzzy eher nein = !eher ja;
	fuzzy f;
	f = eher ja || false;
}
  • Zeile 3:
    • explicit fuzzy(double); ~
  • Zeile 4:
    • fuzzy operator!(); ~
    • fuzzy(fuzzy&&) = default; ~
  • Zeile 5:
    • fuzzy(); ~
  • Zeile 6:
    • fuzzy(bool); ~
    • friend fuzzy operator||(const fuzzy&, const fuzzy&); ~
    • fuzzy& operator=(fuzzy&&) = default;
  • Zeile 7:
    • ~fuzzy() für f, eher_ja, eher_nein, false, und beide von move Konstruktor erstellten Objekte. Also 6 Destrokturen.

c)

friend std::ostream& operator<<(std::ostream& os, const fuzzy& f) 
{
	os << f.truth;
	return os; 
}

d)

#include <array>
 
std::array<fuzzy, 4> a;

In modernem C++ sollte man stattdessen std::array oder std::vector verwenden. Der Grund dafür ist, dass diese Container mehr Funktionalität und Sicherheit bieten als rohe Arrays. Sie verwalten ihre eigene Speicherverwaltung und bieten Methoden zur Größenabfrage, Bounds-Checking und mehr


Aufagbe 6

Makefile cheatsheet
Makefile
PDFLATEX = pdflatex
DATEI = HELLO
RM = rm -f
 
%.pdf: %.tex
	$(PDFLATEX) $<
 
.PHONY: all clean
 
all: $(DATEI).pdf
 
clean:
	$(RM) $(DATEI).pdf $(DATEI).aux $(DATEI).log

Aufgabe 7

POSIX-Funktionen melden Fehler durch Rückgabewerte und die globale Variable errno. Es ist wichtig, nach jedem kritischen Funktionsaufruf die Rückgabewerte zu überprüfen und bei Fehlern errno auszuwerten, um den genauen Fehler zu bestimmen und geeignete Maßnahmen zu ergreifen.
Wenn eine POSIX-Funktion einen Fehler meldet, wird die globale Variable errno auf einen Fehlercode gesetzt, der den spezifischen Fehler beschreibt.
errno muss direkt nach dem fehlerhaften Funktionsaufruf überprüft werden, da nachfolgende Funktionsaufrufe errno überschreiben können.