Mikrorechentechnik 1 Einführung in die Programmiersprache C - Professur für Prozessleittechnik Wintersemester 2010/2011

Die Seite wird erstellt Volker Haas
 
WEITER LESEN
Fakultät Elektrotechnik und Informationstechnik, Professur für Prozessleittechnik

Mikrorechentechnik 1

Einführung in die
Programmiersprache C

Professur für Prozessleittechnik
Wintersemester 2010/2011
Überblick

  • Teil 1 – Variablen und Ausdrücke
        – Schlüsselwörter, Kommentare
        – Vereinbarung von Variablen
        – Ausdrücke und Operatoren

  • Teil 2 – Programmsteuerung
        – Bedingungen, Schleifen, Funktionen

  • Teil 3 – Datenstrukturen
        – Felder, Zeiger
        – Strukturen, dynamische Listen
Weiterführende Literatur

  • Bücher
       – Zeiner, K. (2000) Programmieren lernen mit C.. München:
         Hanser
       – Schellong, H. (2005) Moderne C-Programmierung. Berlin:
         Springer
       – Erlenkötter, (2003) C-Bibliotheksfunktionen sicher
         anwenden. Reinbeck: Rowohlt
       – Kerningham,B., & Ritchie, D. (1983/1990) Programmieren
         in C. München: Hanser

  • WikiBooks
       – http://de.wikibooks.org/wiki/C-Programmierung
       – http://de.wikibooks.org/wiki/C_Sprachbeschreibung
Entwicklung

  1969-1974 Entwicklung Bell Labs (Ritchie)
  1978 Kerningham & Ritchie: The C Programming
  Language: K&R-Standard
  1983 Deutsche Übersetzung K&R
  1989 Standardisierung ANSI-C (C89)
  1990 Übernahme in ISO/IEC 9899:1990 (C90)
  1999 Überarbeitung ISO/IEC 9899:1999 (C99)
  http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
Warum beschäftigen wir uns mit C?

  • Zielsystem eingebettete Systeme
        – Geringer Overhead gewünscht
        – Hardwarenahe Programmierung: Direktes Ansprechen von
          Speicher und Peripherie
        – Portierbarkeit

  • Nachteile Assembler:
        – Programmierung mit prozessorabhängigem Befehlssatz
          und Adressierungsarten
        – Richtige Interpretation von Speicherinhalten in der
          Verantwortung des Programmierers
Stärken von C (1/2)

  • Minimalistischer Sprachumfang
        – Der kleinste bekannte C-Compiler besteht aus 3742 Bytes
          C-Code und kann sich selbst kompilieren.

  • Hardwarenahe Programmierung,
        – direkter Umgang mit Bits, Bytes,
        – direkter Speicherzugriff über Zeiger (=Adressen)
        – Zeigerarithmetik (für die effiziente Behandlung von
          Feldzugriffen)

  • Dynamische Zeiger auf Funktionen
        – (Funktionszeiger) in Datenstrukturen speicherbar
        – flexible Modularisierungskonzept
Stärken von C (2/2)

  • C-Library
        – komplexe Funktionen in standardisierten Bibliotheken für
          viele Architekturen verfügbar.

  • Präprozessor
        – Spracherweiterung: Lesbarkeit!
        – Bedingte Übersetzung: Portabilität.

  • Optimierende C-Compiler
        – für nahezu jede Prozessorarchitektur.

  • Optimierte Embedded Controller
        – Auf C zugeschittene Befehlssätze / Adressierungsarten.
Schwächen von C (1/2)

  • Eingeschränktes Modulkonzept
        – Mit von .c und .h-Dateien notdürftig implementierbar

  • „schwache“ Typsicherheit
  • "Wilde" Zeiger:
        – Nicht initialisierte Variablen möglich

  • Probleme mit Feldern
        – Kein Schutz vor Überlauf
Schwächen von C (1/1)

  • Lücken in der Sprachdefinition
        – Abhängigkeit von Interpretation durch Compilerhersteller

  • Niedriger Abstraktionsgrad
        – Portabilitätsprobleme beim Umstieg von 32 auf 64Bit
Elemente der Programmiersprache C

  • Ziele der Erfinder:
        – Einfach,
        – maximale Flexibilität,
        – leichte Portierbarkeit

  • Komponentenkonzept:
        –   Minimaler Sprachkern +
        –   Präprozessor +
        –   C-Bibliothek für Standardaufgaben +
        –   ( weitere benutzerspezifische Bibliotheken)
Komponente Sprachkern

 • Prozedurale (imperative) Programmiersprache mit
   typisierten Variablen
 • Zeiger (=Adressen) und Zeigerarithmetik
 • Programmflusskontrolle
 • Komplexe benutzerdefinierte Datenstrukturen
 • unabhängig von Befehlssatzarchitektur !
Komponente Präprozessor

  • Direktiven für die Automatisierung von
    „Textbausteinen“
  • Einbinden von Quelldateien
  #include 

  • Textersetzung (Konstanten, Definitionen, Makros)
  #define …

  • Bedingte Übersetzung
  #if defined(UNIX) || defined(CYGWIN)
Komponente Bibliothek

  • Standardbibliothek mit Basisfunktionen zur
    Interaktion mit Betriebssystem und Peripherie
        –   Ein-/Ausgabe,
        –   Dateihandling,
        –   Zeichenkettenverarbeitung,
        –   Mathematik,
        –   Speicherreservierung,
        –   ...

  • ( + Vielzahl von kommerziellen sowie freien
    Funktionssammlungen )
Vom Quellcode zum Programm

 • Programm erstellen
       – Editieren, z.B. mit gedit, eclipse/cdt
       – Ergebnis: Datei hallo.c

 • Übersetzen (Compiler)
       – gcc -g -c hallo.c
       – Ergebnis: Objektdatei hallo.o

 • Binden mit C-Library (Linker)
       – gcc -o hallo hallo.o
       – Ggf. weitere Funktionsbibliotheken: -lm
       – Ergebnis: Ausführbare Datei hallo
C ist EINFACH!

  Der Sprachkern von C besteht aus sehr wenigen
  Schlüsselwörtern, Punktuatoren und Operatoren.
        – 31 Schlüsselwörter
        – etwa 50 Punktuatoren wie () , ;
        – etwa 50 Operatoren wie + - * > <

  Das Strukturierungselement für Programme ist die
  Funktion
  Jedes ausführbare Programm wird mit der Funktion
  main gestartet
Funktionsdefinition

  Jede Funktionsdefinition (auch main) folgt im
  wesentlichen folgender Syntax:
  Rückgabetyp FktName(Argumentliste) // Fkt.Kopf
  {
      Variablenvereinbarung
      Anweisung
      return Rückgabewert;
  }
MRT1-003-C
                                       00_Hallo

Hallo: Vergleich Assembler - C

  /* Hallo in C */        ; Hallo in NASM
  #include       segment .data
                          m: DB "Hallo",10,0
  char *m = "Hallo\n";
                          segment .text
  int main()              extern printf
  {                       global main
      printf(m);          main:
      return 0;               push m
                              call printf
  }                           add esp, 4
                                 mov eax, 1
                                 ret
Schlüsselwörter (1)

  9 SW für elementare   2 SW für zusammen-
  Datenobjekte          gesetzte Datenobjekte
                        struct
  char                  union
  short/int/long
  float/double          1 SW für die Größe von
  signed/unsigned       Datenobjekten
  enum                  sizeof
Schlüsselwörter (2)

  Speicherklasse von      Programmablauf
  Objekten                if / else
  auto                    while
  register                for
  static                  do (do … while)
  extern                  switch
  Typqualifizierung von   case, default
  Objekten                break
  const                   continue
  volatile                goto
                          return
C ist EINFACH!

  31 Schlüsselwörter
  char, short, int, long, float, double, signed,
  unsigned, enum, struct, union, sizeof,
  typedef, auto, register, static, extern,
  const,volatile, if, else, while, for, do,
  switch, case, default, break, continue, goto,
  return

  Die üblichen Operatoren wie
  + - * / = > < & | . [] ? u.s.w

  Einige Punktuatoren wie
  { } () , ; Leerzeichen
C ist nicht immer leicht LESBAR

  • C „versteckt“ die Komplexität in den Operatoren und
    deren Kombinationsmöglichkeiten
  • Je nach Kontext haben gleiche Buchstaben sehr
    unterschiedliche Bedeutung
  *   1. Multiplikation,
      2. Dereferenzierung von Zeiger
  >   1. Relation (Größer),
      2. Im Kontext >> Shift Right-Operation,
      3. Im Kontext -> Verweis auf ein Element einer
              Struktur deren Adresse wir kennen
C ist EFFIZIENT

  • Strukturen, Zeiger und Zeigerarithmetik erlauben
    effiziente Programme (die allerdings oft schwer zu
    verstehen sind)
  • Beispiel Kopiere von einer Zeichenkette zur
    anderen:
  void strcpy(char* srcPtr,char* dstPtr)
  { while(*dstPtr++=*srcPtr++) ; }

  • Eine Vielfalt von Funktionssammlungen steht zur
    Verfügung.
Die hohe Kust der C-Programmierung

  • Einstieg leicht möglich, das erste Programm ist
    schnell geschrieben (z.B. diese Vorlesung)
  • Aber: Das Schreiben portabler (8/16/32/64Bit),
    sicherer (Überlauf, Zeiger) und lesbarer (von
    Menschen) Programme ist nicht trivial!
Fakultät Elektrotechnik und Informationstechnik, Professur für Prozessleittechnik

 Teil1 – Datentypen, Identifier,
 Operationen und Ausdrücke

•   Zeichensatz, Kommentare
•   Identifier (Namen)
•   Datentypen
•   Ausdrücke
•   Operationen
Zeichensatz

  • Buchstaben:      a b c … z A B C … Z

  • Ziffern:         0 1 2 3 4 5 6 7 8 9

  • Sonderzeichen:   !   "   #   % & ' ( )
                     *   +   ,   - . / :
                     ;   <   =   > ? [ ] \ ^ _
                     {   |   }   ~

  • Trennzeichen:    Leerzeichen, Tabulator,
    Zeilenvorschub
  • Achtung: Programmcode ist ein 1-dimensionaler
    Strom aus Tokens
Kommentare

 • Fängt mit /* an, hört mit */ auf
       – Verschachtelung NICHT möglich
       – Ausweg #if 0 ... #endif

 • Seit C99 Zeilenkommentare wie in C++ oder Java:
 • Fängt mit // an, hört mit Zeilenvorschub auf
Identifier=Namen von Variablen und Funktionen

  • Fangen mit einem Buchstaben an (a-zA-Z)
  • Danach sind auch Zahlen und Unterstriche erlaubt
  • Unterstriche können vor dem ersten Zeichen stehen
        – Vorsicht: reserviert für interne Identifier der Bibliotheken!

  • Dürfen keine Schlüsselworte sein
  • Variablen müssen vor Verwendung deklariert sein.
        – Unterschied zu Java/C++: In C müssen die
          Variablenvereinbarung vor der ersten Anweisung des
          Blocks stehen.
MRT1-003-C
                                           01_Datentypen

Datentypen

  • Zeichen                          char

  • Kleine Ganzzahl                  short

  • Ganze Zahl                       int

  • Große Ganzzahl                   long

  Ganzzahlen und Zeichen können als signed/unsigned
  spezifiziert werden (wichtig für Vorzeichenbehandlung)
  • Gleitkommazahl                   float

  • mit doppelter Genauigkeit        double

  • Ab C99: long long, long double
Ausdrücke und Operatoren

  • Zuweisungsausdruck
  Speicherstelle = Ausdruck

  • Arithmetische Ausdrücke
  Ausdruck Operator Ausdruck
  ( Ausdruck )

  • Arithmetische Operatoren
      + - * /      Addition, Subtr., Mult., Division

      %            Modula (Rest der Ganzzahldivision)
Kombinierte Zuweisungsoperatoren

  • Häufig wird Ergebnis einer Operation in einer
    beteiligten Variable abgelegt
  Variable = Variable Operator Ausdruck
  Variable += Ausdruck
  Variable -= Ausdruck
  Variable *= Ausdruck
  Variable /= Ausdruck
  Variable %= Ausdruck
Logische Ausdrücke (1)

  • Gleichheit
  Ausdruck == Ausdruck
  Ausdruck != Ausdruck

  • Relation
  Ausdruck < = > Ausdruck

  • Logische Verknüpfung (nur für Ganzzahltypen)
  ! Ausdruck
  Ausdruck || Ausdruck
  Ausdruck && Ausdruck
Logische Ausdrücke (2)

  • Bis C99 gibt es keinen Wahrheitswert (bool,
    booolean)
        – Wahr: Alle Werte ungleich 0
        – Falsch: 0

  • Achtung: Ergebnis eines logischen Ausdrucks ist
    vom Typ int
        – Wahr = 1
Vorgriff: Vollständige Vorrangregelungen

  • Auswertung normalerweise von links nach rechts
    (Leserichtung!), Ebenen mit (←) rechts nach links:
    Ebene   1: ( ) [ ] -> .
    Ebene   2 (←):! ~ + - (type) sizeof ++ -- * &
    Ebene   3: * / %
    Ebene   4: + -
    Ebene   5: < = >
    Ebene   6: == !=
    Ebene   7: &&
    Ebene   8: ||
    Ebene   9(←): = += -= *= /= %=
Vereinfachte Vorrangregelungen:

  • Auf gleicher Ebene (wenn nicht anders angegeben)
    erfolgt die Auswertung der Operatoren von links
    nach rechts:
    Ebene   1:   ( )
    Ebene   2:   unär ! + - (rechts nach links)
    Ebene   3:   * / %
    Ebene   4:   + -
    Ebene   5:   < = >
    Ebene   6:   == !=
    Ebene   7:   &&
    Ebene   8:   ||
    Ebene   9:   = += -= *= /= %= (rechts nach links)
MRT1-003-C
                                                03_Ausdruck

Beispiel Auswertung

  1 + 4 * 2 / ( 3 + 1 )

  • Ebene 1:    ( )
       – (3+1) wird als eigenständiger Ausdruck behandelt und
         kann „parallel“ zu den anderen berechnet werden

  • Ebene 3:    * / %
       – 4 * 2 / (...) , Auswertung von links nach rechts
       – ( 4 * 2 ) / (...)

  • Ebene 4:    + -
       – 1 + (...)
Typumwandlung (1)

 1) int-Promotion
       – Wenn Wertebereich von int ausreicht, IMMER
         Umwandlung nach int (= „Natürliche“ Größe der
         Plattform)

 2) Umwandlungen, die den Wertebereich nicht
    beschneiden, sind wert- und vorzeichenerhaltend
 3) Wandlung auf den Typ des Nachbaroperators, wenn
    größerer Wertebereich oder ranghöher
 • char, short, int, long, (float), double, long double
MRT1-003-C
                                         03_Ausdruck

Implizite Typumwandlung (2)

  • char und short werden in int umgewandelt, float in
    double
  A) Ist danach einer der beiden Operanden vom Typ
     double, so wird der andere ebenfalls nach double
     gewandelt und das Ergebnis ist vom Typ double
  B) Ist danach einer der beiden Operanden vom Typ
     long, so wird der andere ebenfalls nach long
     gewandelt und das Ergebnis ist vom Typ long
Implizite Typumwandlung (3)

  C) Ist einer der beiden Operanden vom Typ unsigned,
     so wird der andere ebenfalls nach unsigned
     gewandelt und das Ergebnis ist vom Typ unsigned
  • Trifft keiner der Fälle A,B,C zu, sind beide
    Operanden und das Ergebnis vom Typ int
  • Wenn anderes Verhalten gewünscht wird, ist ein
    expliziter Type-Cast notwendig:
  ( type ) Ausdruck
MRT1-003-C
                                         03_Ausdruck

Erweiterte Vorrangregelungen (unvollständig)

  • Auf gleicher Ebene (wenn nicht anders angegeben)
    erfolgt die Auswertung der Operatoren von links
    nach rechts:
    Ebene   1:   ( )
    Ebene   2:   unär ! + - (type)   (rechts nach links)
    Ebene   3:   * / %
    Ebene   4:   + -
    Ebene   5:   < = >
    Ebene   6:   == !=
    Ebene   7:   &&
    Ebene   8:   ||
    Ebene   9:   = += -= *= /= %=    (rechts nach links)
Fakultät Elektrotechnik und Informationstechnik, Professur für Prozessleittechnik

 Teil 2: Steuerung des
 Programmflusses

• Sequenz
• Wiederholung
• Bedingte Ausführung
Programmfluss

  • Lineare Sequenz von Instruktionen +
  • Konstrukte zur Wiederholung, Auswahl, und
    hierarchischen Strukturierung (Funktion)
  Programmiersprache C
  • Sequenz: Liste von Befehlen, Blöcke
  • Wiederholung: while, do…while, for
  • Auswahl: if … else if … else, switch … case
  • Funktionsdefinition und -vereinbarung
Sequenzen und Blöcke

  • Sequenz = Liste von durch SEMIKOLON getrennten
    Anweisungen (statement)
  • Block = Zusammenfassung einer Sequenz durch
    geschweifte Klammern { }.
       – wird von „außen“ wie eine Anweisung gesehen (wichtig
         für Wiederholung und Auswahl).
       – kann lokale Variablen enthalten, die nur im Block bekannt
         sind.
       – { Vereinbarung Statement }
Wiederholung einer Anweisung

  • Kopfgesteuerte (abweisende) Schleife
  while ( Ausdruck ) Anweisung

  • Fußgesteuerte (annehmende) Schleife
  do Anweisung while ( Ausdruck )

  • For-Schleife
  for (Ausdruck1;Ausdruck2;Ausdruck3) Anweisung
For Schleife

  for (Ausdruck1;Ausdruck2;Ausdruck3) Anweisung

  • Ausdruck1: Initialisierung Kontrollvariable(n)
  • Ausdruck2: Testen Abbruchbedingung
  • Ausdruck3: Änderung Kontrollvariable(n)
  • Kurzform für:
     Ausdruck1;
     while (Ausdruck2) {
            Anweisung
            Ausdruck3;
     }
Bedingte Programmausführung: If … else

  • Bedingte Ausführung
  if ( Ausdruck ) Anweisung

  • .. mit Alternative(n)
  if ( Ausdruck )
      Anweisung
  else if ( Ausdruck )
      Anweisung
  else
      Anweisung
Bedingter Ausdruck

  • If … Else ist Anweisung, oft will man aber in
    Abhängigkeit von einer Bedingung das Ergebnis von
    zwei verschieden Ausdrücken haben (ohne
    Zwischenvariable)
  Ausdruck1 ? Ausdruck2 : Ausdruck3

  • Beispiel
  erg = a1 ? a2 : a3;

  • Entspricht in etwa (warum nur „in etwa“?)
  if (a1) erg=a2; else erg=a3;
Fakultät Elektrotechnik und Informationstechnik, Professur für Prozessleittechnik

 Teil 3: Komplexe Daten

• Felder und Matrizen
• Struktur und Union
• Zeiger
Feld

  • Zusammenfassung einer definierten Anzahl
    GLEICHARTIGER Datensätze zu einer Einheit
  • Minimal notwendige Operation: Auswahl eines
    bestimmten Datensatzes über einen Index
Eindimensionale Felder

  • Vereinbarung eines Felds analog Elementarobjekte
  typ identifier [ PositiveGanzzahl ];

  • Elemente werden im Speicher in aufsteigender
    Reihenfolge angelegt
  • Zugriff erfolgt durch []-Operator, Indizierung
    beginnt mit 0!!!!
  identifier[Ausdruck] = wert;
  wert = identifier[Ausdruck];
Mehrdimensionale Felder

  • Vereinbarung
  typ identifier[PosGanzzahl][PosGanzzahl]...;

  • Elemente werden im Speicher in aufsteigender
    Reihenfolge angelegt
  • Letzter Index „wandert“ zuerst
  • Indizierung beginnt mit 0!!!! Zugriff erfolgt durch
  identifier[Ausdruck][Ausdruck]…;
MRT1-003-C
                                            10_vector

Initialisierung von Feldern

  • Deklaration mit Initialisierung:
  type identifier[zahl] = { Liste };

  • Elemente werden mit KOMMA getrennt,
  • Liste muss nicht vollständig sein, nicht erfasste
    Elemente werden mit 0 initialisiert
  • Für mehrdimensionale Felder Verschachtelung von
    Listen :
  int a[2][3] = { {1, 2, 3}, {4, 5, 6} }
Erweiterte Vorrangregelungen (unvollständig)

  • Auf gleicher Ebene (wenn nicht anders angegeben)
    erfolgt die Auswertung der Operatoren von links
    nach rechts:
    Ebene   1:   ( ) [ ]
    Ebene   2:   unär ! + - (type)   (rechts nach links)
    Ebene   3:   * / %
    Ebene   4:   + -
    Ebene   5:   < = >
    Ebene   6:   == !=
    Ebene   7:   &&
    Ebene   8:   ||
    Ebene   9:   = += -= *= /= %=    (rechts nach links)
Strukturen

  • Zusammenfassung einer definierten Menge
    UNTERSCHIEDLICHER Datenobjekte zu einer
    Einheit
  • Beispiel Person
        – Geburtstag, Geschlecht, Adresse, …

  • Beispiel Abteilung
        – Liste von Personen, Räume, Rollen, Budget, …

  • Beispiel Schaltungselement
        – Nummer, Bauteil, Eigenschaftswert,
        – Knoten1, Knoten2, …
Deklaration und Definition

  • Deklaration              • Definition einer
  struct bauteil {             Variablen:
      long number;
                             struct bauteil bt;
      char type;
      double value;          • Deklaration +
      //...                    Definition :
  };                         struct bauteil {
                                 long number;
  • Achtung: Nur der Typ         char type;
    struct bauteil wird          double value;
    deklariert!                  //...
                             } bt;
Initialisierung von Strukturen

  • Initialisierung analog zu Feldern
  struct bauteil {
      long number;
      char type;
      double value;
  };
  struct bauteil bt1 = { 1, 'R', 100 };
  struct bauteil bt[4] = {
      { 1, 'R', 100 },{ 2, 'C', 90 },
      { 3, 'V', 0.9 },{ 4, 'R', 1000 }
  };
Speicherplatzbedarf von Strukturen

  • Tatsächlicher Speicherplatz für eine Struktur ist von
    Compiler und System abhängig
        – Manche Objekte müssen an Adressen ausgerichtet sein,
          die ein Vielfaches von 2 oder 4 sind
        – Compiler füllt automatisch aus

  •
Union

  • Union: Datentyp mit Fallunterscheidung
  • Daten unterschiedlichen Typs und Länge in einem
    Speicherbereich
  • Beispiel: Unterschiedliche Sichten auf einen
    Sende/Empfangspuffer
        – Anwendungsebene: Strukturorientiert
        – Treiberebene: Byteorientiert

  • Vereinbarung wie struct, einfach union anstelle
    Schlüsselwort struct
Sende/Empfangspuffer

  struct snd { // Befehl                  union buffer {
         char code // 1 byte Befehl             struct snd send;
  };                                            struct rcv_vendor recv_vend;
  struct rcv_vendor { // Identifikation         struct rcv_error recv_err;
         int status;                            char code[60];
         long version;                    };
         char vendor[16];
         char model[16];
  };
  struct rcv_error { // Fehlerantwort
         int status;
         long errno;
         char msg[56];
  };
Unterschied zwischen struct und union

  struct t_s {
                            • Welches Ergebnis
       int a;
       char b[4];             liefern sizeof(s) und
  } s;                        sizeof(u)?
  • struct: a,b sind        • Was steht in a nach
    unterschiedliche
                            u.b[0] = 1;
    Speicherplätze
                            s.b[0] = 1;
  union t_u {
       int a;
       char b[4];
  } u;

  • union: a, b am selben
    Speicherplatz!
Bitfelder

  • Können nur innerhalb von struct und union definiert
    werden
  identifier : bit_anzahl

  • Bitfelder folgen in einer Speichereinheit lückenlos
    aufeinander
  • Achtung:
        – Bitfelder haben keine Adresse
        – Reihenfolge der Bits ist nicht spezifiziert
          ( Implementierungsabhängig! )
Beispiel

  struct bitfeld {
      unsigned short a:1, b:2, c:3, d:4;
  } bf = { 1, 2, 4, 8 };
  bf.a = 0;
  bf.d = 11;

  • Auf Intelarchitektur in der Regel wie folgt:
      0000 0010 0010 0101 = 0x0225
      0000 0010 0010 0100 = 0x0224
      0000 0010 1110 0100 = 0x02E4
Leerfelder und Sprünge an Wortgrenzen

  • struct bitfeld {
  • unsigned short a:1, b:2, :4,
  • c:3, d:4, :0,
  • e:5 ;
  • } bf = { 1, 2, 4, 8, 16 };
  • Initialisierung auf Intelarchitektur in der Regel wie
    folgt:
  • 00000000000.1000000.1000.100.0000.10.1
Zeiger

  • Zeiger = Adresse eines Objekts
         – lineares Speichermodell
         – im allgemeinen nicht „unendlich“

  • Voraussetzung für dynamische Speicherverwaltung
  • Voraussetzung für dynamische Funktionszuweisung
Zeiger

  • Variablen werden normalerweise über ihren Namen
    angesprochen
  int summe, i = 1, j = 2;
  summe = i + j;

  • Jede Variable liegt im Speicher an einer bestimmten
    Stelle (Adresse, Aufgabe von Compiler+Linker).
  • Zeiger (auf Variablen) enthalten genau diese
    Adresse einer Variablen
  • Zeiger sind dabei selbst auch „nur“ Variablen
Bestandteile von Zeigern

  • Adresse des Zielobjekts
        – An welcher Stelle im Speicher steht der referenzierte
          Wert?

  • Typ des Zielobjekts (char, int, …)
        – Wie muss der Inhalt an der adressierten Speicherstelle
          interpretiert werden?

  • Größe des Zielobjekts?
        – Wo steht das nächste Zielobjekt (wenn der Zeiger auf ein
          Feld von Zielobjekten zeigte)
Zeiger zeigen auf...

  • vereinbarte Variablen und Funktionen
  • dynamisch reservierten Speicher während der
    Laufzeit
  • virtuellen oder physikalischen Speicher oder
    Peripherie eines Computers (z.B.
    Bildschirmspeicher)
Zeigergrundoperationen

  • Deklaration von
    Zeigern mit typ *
  int a=10;
  int* b, c;
  int *d, e;
  • Adressoperator &
  b = &a

  • Dereferenzierungs-
    operator *
  *b = 11
Zeigerarithmetik (1)

  • Addition/Subtraktion von Zeiger und int
  int a[5] = {0,1,2,3,4}; // int-variable
  int* b = &a[0];          // b zeigt auf a[0]
  int* b = a;              // b zeigt auf a[0]
  b = b + 2;               // b zeigt auf a[2]

  • Increment/Dekrement
  b++;                  // b zeigt auf a[3]
  b -= 2;               // b zeigt auf a[1]

  • Vorrang Incr/Decr vor Deref.
  *b-- = 1                // a[0] = 1
Zusammenhang zwischen Zeigern und Feldern

  • Objektdefinition:   •   Zugriff auf Felder
  int a,i=3;            a   = x[i];
                        a   = *(px+i);
  int x[4];             a   = px[i];
  int *px;
                        • Nächstes Feld:
  • Adresszuweisung:    px++;
  px = x;               x++; //!! Fehler
  px = &x[0];
Zeigerarithmetik 2

  • Vergleich von Zeigern
  int x,a[5] = {0,1,2,3,4};   //   int-variable
  int *b = &a[0];             //   b zeigt auf a[0]
  int *c = &a[2];             //   c zeigt auf a[2]
  if (b != c) { … }           //   <  >= == !=

  • Subtraktion von Zeigern
  x = b – c; // Anzahl Elemente zwischen b und c
Zeiger auf Zeiger

  • Zeiger können auch Zeiger zeigen
  int x, y;
  int *px = &x;          // ptr to int
  int **ppx = &px;       // ptr to (ptr to int)

  •   Zugriff auf das Objekt an Adresse &x
  y   = x;
  y   = *px;
  y   = **ppx;
Rangordnung der Zeigeroperatoren

  • Ausdruck            • mit Klammern
  *++p                  *(++p) = *(p=(p+1))
  *p+1                  (*p)+1
  *&j                   *(&j) = j
  *p**p                 (*p) * (*p)
  -*p                   -(*p)
  a/ *p                 a / (*p)
  a/*p                  /* beginnt Kommentar
Vollständige Vorrangregelungen

  • Auswertung normalerweise von links nach rechts
    (Leserichtung!), Ebenen mit (←) rechts nach links:
    Ebene   1: ( ) [ ] -> .
    Ebene   2 (←):! ~ + - (type) sizeof ++ -- * &
    Ebene   3: * / %
    Ebene   4: + -
    Ebene   5: < = >
    Ebene   6: == !=
    Ebene   7: &&
    Ebene   8: ||
    Ebene   9(←): = += -= *= /= %=
Anwendungsbeispiel: Einfache verkettete Liste

  struct list_el {
      struct list_el *next; // nächstes Element
      double info; // information
  };
  struct liste {
      struct list_el *head; // Kopf der Liste
      struct list_el *tail; // Ende der Liste
  };
Zeichenketten sind Zeiger auf char

  •   Zeichenketten sind (fast immer) char-Felder
  •   Funktionen erkennen Ende am \0-Zeichen
  •   Initialisierung:
  char s1[7] = "ABCD"; char s2[] = "hallo";
  char *s3, *s4;
  s3 = s1; s4 = (char *) malloc(15);

  s3 = "neu"; // !! Fehler
  s3[3] = 'c';
Ein/Ausgabe von Zeichenketten

  •   Funktionen zur Ein/Ausgabe von Zeichenketten
  über Konsole sind in  deklariert:
  char *gets(char *s);
  int puts(const char *s);
  scanf(const char *format, ...);
  printf(const char *format, ...);
  char getchar();
  void putchar(const char c);
Formatangaben

 •   % Formatangabe: Flags Breite.Genauigkeit Modifizierer Umwandlung

 •   Flags:
           – - linksbündig
           – + Vorzeichenbehaftet
           – 0 Mit führenden Nullen

 •   Modifizierer:
           – hh,h,l,ll,L: char/short/long/long long/long double

 •   Umwandlung:
         – Ganzzahl: i, d (dezimal), u (unsigned), o (octal), x (hex), X
           (HEX),
         – Fließkomma: f, e
         – Zeichen: c
         – Zeichenkette: s
         – Adresse: p
Bearbeiten von Zeichenketten

  •    Funktionen zur Manipulation von Zeichenketten
  sind in  deklariert:
  char *strcat(char *dest, const char *src);
  char *strcpy(char *dest, const char *src);
  char *strchr(const char *s, int c);
  char *strrchr(const char *s, int c);
  char *strstr(const char *s1, const char *s2);
  size_t strlen(const char *s);
Felder von Zeigern auf zeichenketten

  •    Zeiger auf Zeichenketten sind Zeiger auf Zeiger
  auf char
  char *worte[4]; // reserviert 4 Zeiger
  char **ppc;       // reserviert 1 Zeiger
  ppc = worte;      // ppc zeigt auf worte[0]

  •   Adressierung von Worten
  char *first, *second;
  first = worte[0];        second = worte[1];
  first = *ppc;            second = *ppc++;
Sie können auch lesen