I-PROGR2-HOME

  1. Einführung

  2. Editor und Compiler

  3. 1-dimensionaler Array versus Vektor-Template

  4. C-String versus String-Klasse

  5. Referenzen statt Zeiger

  6. Dynamisches Speichermanagement mit new

  7. Fortsetzung

  8. Testfragen und Übungen


I-PROGR2 SS03 - PROGRAMMIEREN2 - Vorlesung mit Übung
C++-Umgebung; Arrays und Strings; Referenz-Variablen und Dynamisches Speichermanagement

 Achtung : Skript gibt den mündlichen Vortrag nicht vollständig wieder !!!
                         

AUTHOR: Gerd Döben-Henisch
DATE OF FIRST GENERATION: March-9, 2003
DATE OF LAST CHANGE: March-17, 2003
EMAIL: Gerd Döben-Henisch



1. Einführung


Das Ziel der heutigen Vorlesung besteht darin, aufbauend auf dem Wissen über die Sprache C einige wichtige Unterschiede von C++ gegenüber der Sprache C bekannt zu machen, die noch nicht notwendigerweise ein spezielles Wissen über objektorientiertes Programmieren voraussetzen. Die objektspezifischen Elemente werden dann ab der nächsten Vorlesung nach und nach eingeführt.


START



2. Editor und Compiler


Für die Erstellung von C++-Quellkode wird standardmässig wieder der Editor xemacs benutzt. Zum Kompilieren der GNU-C++-Compiler g++. Eine C++-Quelldatei ('source file') hat die Endung '.cpp'.


START



3. 1-dimensionaler Array versus Vektor-Template


Von der Sprache C her ist das Sprachmittel 'mehrdimensionales Feld' bzw. 'mehrdimensionaler Array' bekannt. Im 1-dimensionalen Fall stellt ein Array eine Folge von Elementen des gleichen Typs im Speicher dar, wobei man auf jedes Element entweder mittels des Array-Index oder mittels seiner Adresse zugreifen kann.

In der Sprache C++ gibt es eine ganze Reihe neuer Sprachelemente, die durch die Standardbibliothek STL ('standard library') zur Verfügung gestellt werden (Das Konzept der Standardbibliothek wird zu einem späteren Zeitpunkt noch ausführlicher besprochen). Eines der neuen Sprachmittel ist das vector-Template. Hier einige Beispiele, welche Funktionalitäten das vektor-Template zur Verfügung stellt.


Hier eine Auswahl der verfügbaren Funktionen:


vector<TYPE> c

Creates empty vector c with no elements

vector<TYPE> c1(c2)

Creates a copy of vector c2 into c1 (same type)

vector<TYPE> c(n)

Creates vector c with n elements

vector<TYPE> c(n,elem)

Creates vector c with n copies of element elem

vector<TYPE> c(beg,end)

Creates a vector c initialized with the elements of the range [beg,end)

c.~vector<TYPE>()

Destroys all elements and frees the memory

c.size()

Returns the actual number of elements

c.empty()

Returns whether the vector is empty c

c.max_size()

Returns the maximum number of elements possible

capacity()

Returns the maximum number of elements possible without reallocation

reserve()

Enlarges capacity if not enough

c1 == c2

Whether equal

c1 != c2

Not equall

c1 < c2

Lesser than

c1 > c2

Greater than

c1 >= c2

greater than or equal

c1 <= c2

lesser than or equal

c1 = c2

Assigns all elements of c2 to c1

c.assign(n,elem)

Assigns n copies of elem to c

c.assign(beg,end)

Assigns the elements of the range beg - end

c1.swap(c2)

Swaps the data of c1 and c2

swap(c1,c2)

Swaps the data of c1 and c2

c.at(idx)

Returns the element with index idx. In case of 'out of range' throws an error (the only range checking function !)

c[idx]

Returns element with index idx (no range checking!)

c.front()

Returns the first element (no range checking!)

c.back()

Returns the last element (no range checking!)

c.push_back(elem)

Appends a copy of element elem at the end

c.pop_back()

Removes the last element (does not rrturn it)

c.erase(pos)

Removes the element at position pos and returns position of next element

c.resize(num)

Changes the number of elements to num; new elements are created by the default constructor

c.resize(num,elem)

Changes the number of elements to num; new elements are created as copies of elem

c.clear()

Removes all elements



Dazu ein einfaches Beispiel, das nur einen winzigen Bruchteil der verfügbaren Möglichkeiten nutzt.





//---------------------------
//
// ArrayDemo.cpp
//
// Author: m.a.weiss
// Modifications: g.doeben-henisch
//
// compilation: g++ -o   ArrayDemo ArrayDemo.cpp 
// usage:  ArrayDemo
//
//--------------------------------- 



#include <iostream>
#include <vector>
using namespace std;


// Generate numbers (from 1-100)
// Print number of occurrences of each number
int main( )
{
    const int DIFFERENT_NUMBERS = 100;

      // Prompt for and read number of games.
    int totalNumbers;
    cout << "How many numbers to generate?: ";
    cin >> totalNumbers;

    vector<int> numbers( DIFFERENT_NUMBERS + 1 );

      // Initialize the vector to zeros.
    for( int i = 0; i < numbers.size( ); i++ )
        numbers[ i ] = 0;

      // Generate the numbers.
    for( int j = 0; j < totalNumbers; j++ )
        numbers[ rand( ) % DIFFERENT_NUMBERS + 1 ]++;

      // Output the summary.
    for( int k = 1; k <= DIFFERENT_NUMBERS; k++ )
        cout << k << " occurs " << numbers[ k ]
                  << " time(s)\n";

    return 0;
}




//---------------------------
//
// GetInts.cpp
//
// Author: m.a.weiss
// Modifications: g.doeben-henisch
//
// compilation: g++ -DRESIZE[=0] -o GetInts GetInts.cpp
// usage: GetInts
//
//---------------------------------

#include <iostream>
#include <vector>
using namespace std;

#if RESIZE

// Read an unlimited number of ints with no attempts at error
// recovery; fill the vector parameter with the data; its size
// after the return tells how many items were read.
void getInts( vector<int> & array )
{
    int itemsRead = 0;
    int inputVal;


    cout << "case cin  " << RESIZE << endl;

    cout << "Enter any number of integers: ";
    while( cin >> inputVal )
    {
        if( itemsRead == array.size( ) )
            array.resize( array.size( ) * 2 + 1 );
        array[ itemsRead++ ] = inputVal;
    }
    array.resize( itemsRead );
}

#else

// Same as before, but use push_back
void getInts( vector<int> & array )
{
    int inputVal;

    array.resize( 0 );

    cout << "Case push-back" << RESIZE << endl;

    cout << "Enter any number of integers: ";
    while( cin >> inputVal )
        array.push_back( inputVal );
}

#endif

// Test getInts.
int main( )
{
    vector<int> array;

    getInts( array );
    for( int i = 0; i < array.size( ); i++ )
        cout << array[ i ] << endl;

    return 0;
}




START



4. C-String versus String-Klasse


Neben dem Konzept Zeichenkette ('string'), wie es aus der Sprache C bekannt ist --C-String genannt--, gibt es in C++ auch eine neue Klasse 'String'.

Hier einige ihrer Funktionen:

Hier eine Auswahl der verfügbaren Funktionen:


string c

Creates empty string c with no elements

string c1(c2)

Creates a copy of string c2 into c1

string c(n,a)

Creates a string c with n copies of character a

string c(beg,end)

Creates a string c initialized with the characters of the range [beg,end)

c.~string()

Destroys all elements and frees the memory

c.size()

Returns the actual number of elements

c.empty()

Returns whether the string is empty c

c.max_size()

Returns the maximum number of elements possible

c.length()

Returns the number of elements

c.reserve()

Enlarges capacity if not enough

c1 == c2

Whether equal

c1 != c2

Not equall

c1 < c2

Lesser than

c1 > c2

Greater than

c1 >= c2

greater than or equal

c1 <= c2

lesser than or equal

c1 = c2

Assigns all elements of c2 to c1

c.assign(n,elem)

Assigns n copies of elem to c

c.assign(beg,end)

Assigns the elements of the range beg - end

c1.swap(c2)

Swaps the data of c1 and c2

swap(c1,c2)

Swaps the data of c1 and c2

c.at(idx)

Returns the element with index idx. In case of 'out of range' throws an error (the only range checking function !)

c[idx]

Returns element with index idx (no range checking!)

c.getline()

Read the value of a stream

c.push_back(elem)

Appends a copy of element elem at the end

c1 + c2

Concatenates two strings

c.resize(num)

Changes the number of elements to num; new elements are created by the default constructor

c.clear()

Removes all elements

c.c_str()

Return the string as C-String

c.data()

Return the string as an array of characters without terminating 0.


Hier ein einfaches erstes Beispiel:







//---------------------------
//
// TestString.cpp
//
// Author: m.a.weiss
// Modifications: g.doeben-henisch
//
// compilation: g++ -o  TestString TestString.cpp 
// usage:  TestString
//
//---------------------------------



#include <iostream>
#include <string>
using namespace std;

int main( )
{
    string a = "hello";
    string b = "world";
    string c;             // Should be ""

    c = a + " ";          // Should be "hello "
    c += b;               // Should be "hello world"

      // Print c the easy way.
    cout << "c is: " << c << endl;

      // Print c the primitive way.
    cout << "c is: " << c.c_str( ) << endl;

      // Print c character-by-character.
    cout << "c is: ";
    for( int i = 0; i < c.length( ); i++ )
        cout << c[ i ];
    cout << endl;

    return 0;
}



START



5. Referenzen statt Zeiger


Ein weiteres neues Sprachmittel in C++ ist die Referenz. Eine Referenz ist quasi ein 'Stellvertreter' für eine andere Variable; einmal vereinbart, kann man sie nicht mehr ändern. Im Kontext der Argumentübergabe an eine funktion arbeitet dieser Mechanismus wie die Übergabe einer Adresse, aber man benötigt weder den Inhalts-Operator '*' noch den Adressoperator '&'. Die Syntax ist sehr einfach:

TYPE1 bezug;
TYPE1 & referent = bezug;





//---------------------------
//
// TestSwap.cpp
//
// Author: m.a.weiss
// Modifications: g.doeben-henisch
//
// compilation: g++ -o  TestSwap TestSwap.cpp 
// usage:  TestSwap
//
//--------------------------------- 

<iostream>
using namespace std;


// Does not work
void swapWrong( int a, int b )
{
    int tmp = a;
    a = b;
    b = tmp;
}

// C Style -- using pointers
void swapPtr( int *a, int *b )
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

// C++ Style -- using references
void swapRef( int & a, int & b )
{
    int tmp = a;
    a = b;
    b = tmp;
}

// Simple program to test various swap routines
int main( )
{
    int x = 5;
    int y = 7;

    swapWrong( x, y );
    cout << "x=" << x << " y=" << y << endl;
    swapPtr( &x, &y );
    cout << "x=" << x << " y=" << y << endl;
    swapRef( x, y );
    cout << "x=" << x << " y=" << y << endl;

    return 0;
}


START



6. Dynamisches Speichermanagement mit new


Gegenüber C wurde das dynamische Speichermanagement vereinfacht. Es gibt nur noch die Operatoren 'new' und 'delete'.


TYPE Pointer = new TYPE

Erzeugt mit dem Befehl 'new' einen Speicherbereich geeigneter Grösse vom Typ TYPE

Dazu schreibt der Standard:

"Entities created by a new-expression have dynamic storage duration (3.7.3). [Note: the lifetime of such an entity is not necessarily restricted to the scope in which it is created. ] If the entity is a non-array object, the new-expression returns a pointer to the object created. If it is an array, the new-expression returns a pointer to the initial element of the array."

delete pointer

Hebt mit dem Befehl 'delete' den Speicherbereich wieder auf, auf den der Pointer pointer zeigt; pointer muss zuvor von new erzeugt worden sein.

Einige Beispiele zum Gebrauch von 'new' und 'delete':

float *fp = new float;     //Erzeugt einen speicherbereich für eine Gleitkommazahl
delete fp;                //Gibt den Speicherbereich wieder frei

char *s = new char[len+1]; //Legt ein Feld für Elemente vom Typ char an
delete [] s;               //Freigabe des Feldes mit den Elementen vom Typ char. Achtung: bei Feldern müssen vor dem Feldnamen die
                           //eckigen Klammern '[]' gesetzt werden

 

Dazu das Beispielprogramm bzeile3.cc. Die Eingaben an das Programm bzeile3 werden komplett über die Befehlszeile abwickelt. Das Schema der Befehlszeile lautet:

bzeile3 YDIM XDIM SEED MAXRUN

Ein Aufruf von bzeile3 wäre dann z.B. der folgende:

bzeile3 10 10 123 100
 

Nachdem die Argumente an das Programm mit Namen 'bzeile3' übergeben worden sind, werden diese vom Programm zunächst automatisch in C-Strings umgewandelt und in dem Feld '*argv[]' gespeichert, d.h. jedes Element des Feldes ist ein C-STring. Da es sich bei der Eingabe aber um Zahlen handelt, müssen diese Zahlen im Stringformat in ein Zahlenformat umgewandelt werden. Dies geschieht im Programm mit der Funktion 'atoi()' (ASCII-to-Integer). Schliesslich muss noch das 2D-Feld mit der gewünschten Dimensionierung im Speicher angelegt werden. Dies geschieht mit 'new'. Ab dann läuft das Programm genauso wie das Programm rprojekt1.cc. Die Hilfsfunktionen in den Dateien rand1.cc, show2.cc sowie count2d.cc wurden nicht verändert. Hier ist der Quellcode zu bzeile3.cc:


//*************************
//
// bzeile3.cc
//
//  DATUM: 12. April 2002
// ÄNDERUNG: 12. April 2002
//
// You  can  give arguments on the commandline which are numbers and these will be counted, printed on the 
// screen, converted to numbers and then used to initialize a 2D-array and to guide the program.
// The program runs a random number generator to hit the elements of a 2D-array.
// After a given number of runs (maxrun) the program stops and counts the elements which have been not hit
// by an random event
//
// compile: g++ -o bzeile3 bzeile3.cc rand1.cc show2.cc count2d.cc
// usage: bzeile3 YDIM XDIM SEED MAXRUN
//
//*******************************

#include <iostream>
#include <cstdlib>

#include "bzeile3help.h"


int main(int argc, char *argv[]){

 int rx,ry;                            //aktuelle Zufallszahlen für die Y-Richtung und für die X-Richtung eines 2D-Feldes
 float upper_x, upper_y;               //Groesste Zufallszahl, die für die Y- bzw. X-Richtung erzeugt werden soll

  if (argc > 1) {
    std::cout << "Programm " << argv[0] << " hat " << argc-1 << " weitere(s)  Argument(e)." << std::endl;

    for(int i=1; i<argc; i++){
      std::cout << i << " = " << argv[i]  << std::endl;
    }

    if (argc == 5){ //Argumentzahl stimmt; Inhalt ist nicht geprüft!

      const unsigned int yDim = atoi(argv[1]); //Setzen der y-Dimension
      const unsigned int xDim = atoi(argv[2]); //Setzen der x-Dimension
      const unsigned int seed =atoi(argv[3]); //Setze eine Initialisierungszahl für die Zufallszahlenfolge
      const unsigned int maxrun =atoi(argv[4]); //Maximale Zahle von gewuenschten Zufallsereignissen

      upper_x = xDim * 1.0; //Umwandlung von unsigned int in float
      upper_y = yDim * 1.0;

      int (*ap)[xDim] = new int[yDim][xDim]; //new generiert neuen Speicher; liefert einen Zeiger auf ein Feld zurück

      srand(seed);  //Startet neue Zufallszahlenfolge

      for(int i=0; i<maxrun; i++) { //Hauptschleife

        ry = rand1(upper_y)-1; //Übernahme einer Zufallszahl für die y-Dimension
        rx = rand1(upper_x)-1; //Fürprechend fuer die x-Dimension

               std::cout << "y =" << ry << " , x = " << rx << std::endl;

                ap[ry][rx] ++;

                 std::cout << "Runde "<< i << "-----------------------------------------" << std::endl;

                 show2(&ap[0][0], yDim, xDim) ;

                  for(int j=0; j<25000000; j++) {;} //Verzögerungsschleife, um Ereignisse 'sehen' zu koennen

                }//End of for i

  std::cout << "-------------------------------------------------------------------------------------------" << std::endl;

  std::cout << "Prozentsatz der NICHT besetzten Felder = " << ( count2d(&ap[0][0], yDim, xDim) * 1.0) / ((yDim * xDim * 1.0) / 100.0 ) << std::endl;

 std::cout << "-------------------------------------------------------------------------------------------" << std::endl;


      delete [] ap;     //Hebt den Speicher wieder auf
    }

  }
else {
  std::cout << "Programm " << argv[0] << " hat keine zusaetzlichen  Argumente." << std::endl;
}


}


Makefile

LD = g++

CPPFLAGS = -g

objects = bzeile3.o rand1.o show2.o count2d.o


 bzeile3: $(objects)
	$(LD)  -o  bzeile3 $(objects)

 bzeile3.o:  bzeile3help.h

clean:
	rm  bzeile3 $(objects)




Show Content of Array

//*************************
//
// show2.cc
//
// first: april-11, 2002
// last: april-11, 2002
//
// Shows the content of a 2D-Array with the dimensions
// yDim x xDim
//
// compile: g++ -c show2.cc
// call: show2(&Array-Name[0][0], yDim, xDim)
//*******************************

#include <iostream>

void show2(int *ap, int yDim, int xDim){

  int x,y;                                          //Indexvariablen für die for-Schleifen

 std::cout << "-------------------------------------------------------------------------"<<   std::endl;



for( y=0; y <yDim; y++) {
  for( x=0; x<xDim; x++){

       std::cout << *(ap + (y * xDim) + x);

   }//End of x-loop

       std::cout <<  std::endl;

 }//End of y-loop
}


Random Generator with upper limit

/****************************
*
* rand1.cc
*
* Ein Zufallsgenerator mit oberer Schranke und Anzahl der Würfe
*
*
* COMPILATION: g++ -c  rand1.cc
* CALL: rand1(upper)
*
*********************************/
#include <cstdlib>

int rand1 (float upper){

  /* Da rand1() eine Zufallszahl r zwischen 1 und RANDS_MAX  liefert, muss diese Zahl r
   * auf den gewünschten Bereich von [1,upper] umgerechnet werden. Die 'Numerical Recipes in C'
   * empfehlen die untenstehende Formel:
 */

    return (1+(int)( upper*rand() /(RAND_MAX+1.0  ) ));

}


Counter of numbers in Array

//*************************
//
// count2d.cc
//
// first: april-11, 2002
// last: april-11, 2002
//
// Counts the zeros in a 2D-Array with the dimensions
// yDim x xDim 
//
// compile: g++ -c count2d.cc
// call: count2d(&Array-Name[0][0], yDim, xDim)
//*******************************

#include <iostream>

unsigned int  count2d(int *ap, int yDim, int xDim){

  int x,y;                                          //Indexvariablen für die for-Schleifen
  unsigned int counter;

 counter = 0;

for( y=0; y <yDim; y++) {
  for( x=0; x<xDim; x++){

    if (*(ap + (y * xDim) + x) == 0) counter++;

   }//End of x-loop
 }//End of y-loop

 return counter;

}


START



7. Fortsetzung


Nachdem durch konkrete Beispiele einige der gegenüber C neuen Sprachelemente von C++ eingeführt worden sind, soll ab der nächsten sitzung des Konzept der objektorientierten Programmierung sowie dessen Realisierung mittels der Sprache C++ im vordergrund stehen.


START



8. Testfragen und Übungen


  1. Wie unterscheiden sich die Compileraufrufe von C und C++?


  2. Was unterscheidet einen Vektor von einem Array?


  3. Wie gibt man in C++ etwas auf den Bildschrimn aus?


  4. Wie liest man in C++ etwas von der Tastatur in eine Variable?


  5. Mit welchen Befehlen können Sie einen Vektor vergrössern oder verkleinern?


  6. Wie kopieren Sie einen Vektor in einen anderen?


  7. Wie können sie einen Vektor mit einem bestimmten Anfangswert initialisieren?


  8. Wie können Sie in einem Vektor alle Elemente löschen ohne den Vektor zu löschen?


  9. Wie beseitigen Sie einen Vektor?


  10. Was unterscheidet einen String in C++ von einem C-STring?


  11. Wie legen Sie in C++ Strings an?


  12. Wie können Sie einen String initialisieren?


  13. Wie verketten Sie zwei Strings c1 und c2?


  14. Wie kopieren sie einen String c2 nach c1?


  15. Wie können Sie Strings c1 und c2 vergleichen?


  16. Was ist eine Referenz?


  17. Konstruieren Sie ein Beispiel, in dem sie den Gebrauch von Pointern durch die Referenz ersetzen.


  18. Welche Operatoren benutzt C++ für das dynamische Speichermanagement?


  19. Wie reservieren Sie Speicher für eine einfache Variable vom Typ T?


  20. Wie reservieren Sie Speicher für einen Array vom Typ T?


  21. Wie reservieren Sie Speicher für einen 3-dimensionalen Array vom Typ T


  22. Welche Alternative kennen Sie, wenn Sie Speicher für einen 2-dim Array reservieren wollen ohne mit zwei Dimensionen arbeiten zu wollen?


  23. Wie ist ein einfaches Makefile aufgebaut?


  24. Benutzen Sie das Makefile zum Beispielprogramm der dynamischen Speicherreservierung und löschen sie alle Objektfile.


  25. P1: Schreiben Sie eine kleine Routine, bei der Sie über die Tastatur beliebig viele Zahlen eingeben können; benutzen sie für die Speicherung der Zahlen einen Vektor.


  26. P2a: Schreiben Sie eine kleine Routine, bei der Sie über die Tastatur beliebig viele Worte eingeben können. Benutzen Sie zur Speicherung den C++-Datentyp 'string'. Nach Abschluss der Eingabe sollen die Worte sortiert und auf den Bildschirm ausgegeben werden.


  27. P2b: Erweitern Sie das Programm P2a dahingehend, dass das Programm erkennt, wenn sich Worte in Buchstaben überlappen.Die Ausgabe der Liste soll so sein, dass (1) das Wort in der Liste ausgedruckt wird, dann (2) alle Worte, die sich mit diesem Wort überlappen.


  28. P3: Schreiben Sie das Programm P2b so um, dass das Programm mindestens eine externe Funktion umfasst, deren Argumente Referenzen sind. Mittels der Referenzen sollen Variablen des Hauptprogramms manipuliert werden.



START