statische <-> dynamische Variablen

statische Variablen

<type> <name> [<value>]

– Sie haben eine fixe Grösse
– Sie werden über den Namen aufgerufen
– Ihre Gültigkeit hängt von ihrem Definitionsort ab:
.  lokal: nur in Funktion gültig
.  global (extern): im ganzen Programm
Haben nichts mit dem Keywort static zu tun
Sie liegen im Datensegment

.   <<Statische Variablen hängen von der
.        (statischen) Programmstruktur ab.>>

dynamische Variablen
– Haben keinen fixen Namen: Es gibt keine Variablenvereinbarung (Definition)
– Können nur über den Return-Pointer der Funktion malloc() bearbeitet werden
– Sie kennen keine Funktions- oder Dateigrenzen, sondern sind überall sichtbar
– Eine dynamische Variable ist durch das Programmende oder durch die Funktion free( Variablen-Pointer) vernichtbar
– Speicherort ist der HEAP

Dynamische Speicherzuweisung C: „Allocation“

Nicht dynamisch
Definiert man eine Variable, Konstante oder ein Array mit fixer Länge char array[5] , so ist die Länge des Speicherplatzes klar und die Speicherzuweisung ist nicht dynamisch.
Nicht dynamische Speicherzuweisungen werden im STACK abgelegt.

Grund für dynamisch
Die Grösse des Arrays ändert sich während der Verarbeitung. Man kann das Array nicht auf eine fixe Grösse initialisieren.
Eine dynmische Speicherzuweisung nennt man Allozierung. Die Daten werden im HEAP abgelegt.

Vorgehen
1. Speicherplatz reservieren
Dies geschieht über die Funktion malloc().  Es ist zwingend, dass man die Anzahl Bytes nennt.  int *buffer1 = malloc(n * sizeof(data));.  N bezeichnet die Anzahl daten, data bezeichnet die Grösse eines Datenpackets.
Die Funktion sizeof( )  gibt immer die Anzahl Bytes der Variable zurück.
2. Auf dynamische Daten zugreiffen
Die Funktion malloc() gibt als Returnwert die Adresse zurück. Auf dynamische Daten kann nur über den Pointer zugegriffen werden. Ein direkter Zugriff über den Variablennamen auf die Daten ist nicht möglich. data1 = buffer1[0];
3. Speicherplatz freigebe
free(buffer1); Wird Speicherplatz nicht mehr freigegeben, so entsteht ein Memory Leak. Ohne free() verkleinert sich die Speicherkapazität des RAMs.

Speicherorte: Die „Segmente“ im RAM

Grundstruktur Datenablage
Ein ausführbares Programm (*.exe) teilt seine Daten in vier Segmente ein: *.code, *.data, *, heap und Stack.

code_data_heap code_data_heap_chinese

Codesegment
Liegt bei der tiefsten Adresse. Hier liegt der Maschinencode. Häufig liegen hier auch die Konstanten. Das Codesegment kann mit einem Schreibschutz versehen werden, also nur gelesen werden.

Datensegment
Hier liegen die initialisierten globalen Variablen sowie wenn eine lokale Variable als static definiert wurde. Sind Variablen nicht initialisiert, so liegen sie im BSS Segment.

Heap
Enthält nur dynamische Variablen, die mit malloc() zur Laufzeit generiert und wieder gelöscht werden.

Stack
Legt alle sich ändernden Daten zur Laufzeit ab und holt sie permanent wieder. Ständiges Wechseln der Daten. Deshalb braucht es einen Stack Pointer, der jeweils die aktuelle Speicherstelle weiss.
Der Stack liegt an der höchsten Adresse.
Im Stack liegen alle lokalen Variablen.

 

 

Pointers in C

Durch das Setzen eines Asterisk vor einer Variable, definiert man, dass die Variable keinen Wert, sondern eine Adresse beinhaltet:

  • Zeigen auf eine Speicheradresse
  • Sie beinhalten eine Adresse
    <type> *<name>, *<name>;

    Adresse übergeben (referenzieren)

    int *pointer;
    pointer = &variable_a;
    

    Die Variable_a wird gelesen, aber nur ihre Adresse wird übergeben.

    Wert übergeben (dereferenzieren, per value)

    *pointer = 10;
       // oder
    var1 = *pointer_b;
    printf(" %d ",  *pointer)

    Der Wert wird ebenfalls über einen Stern zurückgegeben. Das ist verwirrlich, zeigt aber auch, dass es ein Wert von einem Pointer ist…

    —————————————————————————–

    Pointer a

    int *a;
    int b;

    Wohin zeigt a ?

    b = &a;  // b hat die Adresse

    a soll einen Wert erhalten

    *a = 10;

Keyword static in C

static lokale Variable
– Ziel:
Die Variable behält ihren Wert zwischen zwei lokalen Funktionsaufrufen.
Die Initialisierung gilt nur beim ersten Mal (Ablegen auf dem Datensegment).
– Sichtbarkeit:
Der Gültigkeitsbereich bleibt gleich. Der Ablageort der Variable ist auf dem Datensegment anstelle des Stacks.
– Bsp: Zählen von Interrupts

<type> function( ){
.       static int nr_of_calls = 0;
.
.       // do some code
.
       nr_of_calls ++;
.      return <type>;
}

 

static globale Variable
– Ziel:
Wie bei der lokalen statischen Variable. Zwischen den Aufrufen behält die Variable den Wert. Wenn sie global ist, dann behält sie den Wert während der ganzen Programmdauer.
– Sichtbarkeit:
Eine externe Variable mit static ist ausserhalb der Datei unsichtbar!

 

static Funktion
– Sichtbarkeit:
Wird eine Funktion extern und static gesetzt, dann ist sie ausserhalb der Datei nicht sichtbar.

Unit Testing with Catch

Es gibt CATCH, das Testing für C++ ermöglicht. Es gibt einen Blogeintrag, der den Einsatz von CATCH auf hohem Niveau beschreibt. Zum Starten ist das Tutorial sehr gut.

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

unsigned int Factorial( unsigned int number ) {
    return number <= 1 ? number : Factorial(number-1)*number;
}

TEST_CASE( "Factorials are computed", "[factorial]" ) {
    REQUIRE( Factorial(1) == 1 );
    REQUIRE( Factorial(2) == 2 );
    REQUIRE( Factorial(3) == 6 );
    REQUIRE( Factorial(10) == 3628800 );
}

Catch innerhalb eines Files einbauen und ausführen

1. Catch als Header in File einbinden

#define CATCH_CONFIG_RUNNER  
#include "catch.hpp"

2. Funktion, die getetst werden will

double calculate(string calculation) {
    double result = 0.0;
    ts.set(calculation);
    result = expression();
    return result;
}

3. Testfälle aufstellen

TEST_CASE( "r", "[digit]" ) {
    REQUIRE(calculate("4") == 4);
    REQUIRE(calculate("6") == 6);
    REQUIRE(calculate("4+2") == 6);
    REQUIRE(calculate("4+2+2") == 8);
    REQUIRE(calculate("4+2+2+4+2+2+1") == 17);
    REQUIRE(calculate("4-2") == 2);
    REQUIRE(calculate("8-2") == 6);
    REQUIRE(calculate("8-2-2") == 4);
    REQUIRE(calculate("3*2") == 6);
    REQUIRE(calculate("3*3") == 9);
    REQUIRE(calculate("3*2*3") == 18);
    REQUIRE(calculate("3+2*4") == 11);
    REQUIRE(calculate("3*2+4") == 10);
    REQUIRE(calculate("4 + 2") == 6);
    REQUIRE(calculate("10 + 2") == 12);
    REQUIRE(calculate("10 + 123") == 133);
    REQUIRE(calculate("-4 + 5") == 1);
    REQUIRE(calculate("-100 + 5") == -95);
    REQUIRE(calculate("(4 + 5)*2") == 18);
    REQUIRE(calculate("3+(-4 * 5)") == -17);
    REQUIRE(calculate("1.5 * 2") == 3);
}

Am Ende des Files folgend die Testfälle.

4. Datei kompilieren
5. Ausführen über Konsole und Argumente mitgeben
Gibt man keine Argumente mit, werden alle Tests durchgelauften.

Struktur der Testfälle

REQUIRE(calculate("6") == 6);

Nach REQUIRE schreibt man die Funktion, die aufgerufen werden soll und gibt ihr die notwendigen Werte mit.
Nach dem == folgt das zu erwartende Ergebnis.

Basic Error-Library

#include <exception>

Der Standardteil dieser Bibliothek umfasst folgende Exceptions:
Logische Fehler                                 Runtime Fehler
– logic_error                                         – runtime_error
– domain_error                                  – range_error
– invalid_argument                          – overflow_error
– length_error                                    – underflow_error
– out_of_range

In der Funktion

void Token_stream::put_back(Token t) {
    if (full) {
        throw std::overflow_error("buffer already full");
    }
    full = true;
    buffer = t;
}

 

 

own Error-Class (Basics)

class neg_sum{};

int main(){
  try {
.     ...
.     
.     ...
  } catch(neg_sum const & e){
.     std::cout << "negative value"<<"\n";
  }   
}

In der Funktion

double solve_quadratic(int a, int b, int c) {
    double delta = pow(b, 2) - (4 * a * c);
    if (delta < 0) {
        throw Negative_delta{};
    }
.   .....
}

 

VHDL Code von Speicher starten

Datei.pof
Der Speicher braucht ein bistream als Synthese.  Deshalb muss neben der *.sof-Datei auch eine *.pof-Datei erzeugt werden.

Grundeinstellung ändern
Bei der Synthese, soll gleich auch die *.pof-Datei generiert werden.
Assignments/Device     Taste: Device and Pin Options    /Configuration
Auswählen von:           Active Serial
Hacken bei:                     v   Use configuration device:       Auto (oder auswählen)
.                                            v   Generate compressed bitstream


Konvertieren *.sof zu *.pof

File/Convert Programming Files…/    Folgende EinstellungeProgramming file type:        Programmer Object File (.pof)
Configuration device:           (siehe Namen auf Board)
File name:                                  <pfad>/name.pof
Input files to convert:            – Anklicken SOF Data
.                                                      – Add File..
.                                                      – outputfile/datei.sof  aus Ordner Synthese
Generate

Programmer Einstellungen
Mode:                                          Active Serial Programming
File:                                              *.sof löschen
.                                                      *.pof adden
Device:                                          <name des Speichers> ! nicht des FPGA

Auf Board
!!!!! Schalter auf Programm !!! nur so entsteht Verbindung zu Speicher.