Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 15 Strukturen
Pfeil 15.1 Struktur deklarieren
Pfeil 15.2 Initialisierung und Zugriff auf Strukturen
Pfeil 15.3 Strukturen als Wertübergabe an eine Funktion
Pfeil 15.4 Strukturen als Rückgabewert einer Funktion
Pfeil 15.5 Strukturen vergleichen
Pfeil 15.6 Arrays von Strukturen
Pfeil 15.7 Strukturen in Strukturen (Nested Structures)
Pfeil 15.8 Kurze Zusammenfassung zu den Strukturen
Pfeil 15.9 Unions
Pfeil 15.10 Der Aufzählungstyp »enum«
Pfeil 15.11 Typendefinition mit »typedef«
Pfeil 15.12 Attribute von Strukturen verändern (nicht ANSI C)
Pfeil 15.13 Bitfelder
Pfeil 15.14 Das »offsetof«-Makro


Rheinwerk Computing - Zum Seitenanfang

15.13 Bitfelder topZur vorigen Überschrift

Bitfelder sind Strukturelemente, die mit weniger als 1 Byte in eine Struktur gepackt werden können. Laut ANSI C müssen die einzelnen Elemente von Bitfeldern vom Datentyp int oder unsigned int sein.

Als Beispiel soll hier ein Roboter für eine Fließbandproduktion programmiert werden. Der Roboter muss ein bestimmtes Produkt von Position A nach Position B transportieren. Dazu sind folgende Arbeitsabläufe erforderlich:

1. Produkt zum Befördern vorhanden (Sensor 1 = aktiv). Wenn ja …
2. Produkt erfassen und hochheben und auf das Fließband legen. Wenn Produkt auf Fließband liegt (Sensor 2 = aktiv), dann ...
3. Fließband bewegen (Schalter 1) und warten, bis Produkt beim Sensor 3 ankommt. Dann wieder prüfen, ob ein Produkt auf Sensor 1 vorhanden ist. Falls an Sensor 1 ein Produkt vorhanden ist, alles wieder von vorn.

Dieser Vorgang soll auch überprüft werden, und bei einem Fehler wird ein Fehlercode ausgegeben, für den es eine entsprechende Stringtabelle gibt. Außerdem wird dazu eine Anzeige benötigt. Es wird davon ausgegangen, dass der Roboter bereits mit dem PC verbunden ist. Hier sehen Sie die Struktur des Roboters:

struct robo {
   unsigned char sensor1;
   unsigned char sensor2;
   unsigned char sensor3;
   unsigned char schalter;
   unsigend int Ausgabe;
} Roboter1;

Mit dieser Struktur benötigt der Roboter 48 Bits (6 Bytes bzw. 8 Bytes wegen des Alignments des Betriebssystems). Wenn jetzt noch mehrere Roboter hinzukommen, ist das eine Speicherplatzverschwendung, und Speicherplatz ist bei solchen Systemen meistens kostbar. Häufig ist bei solchen Automatisierungsrobotern nicht unbegrenzt Speicherplatz vorhanden. Bei den Sensoren und Schaltern benötigen Sie in der Regel nur zwei Schaltstellungen: 1 für »betätigt« und 0 für »unbetätigt«. In C ist es auch möglich, einzelne Bits einer Struktur mit sogenannten Bitfeldern anzusprechen.

Die Deklaration eines solchen Bitfelds hat folgende Syntax:

Typ Elementname : Breite ;

Für den Typ können Sie einen beliebigen Ganzzahltyp wie int, signed int, unsigned int oder _Bool verwenden. Sie können auch Typqualifizierer nutzen. Den Elementnamen benötigen Sie, um auf das Bitfeld zugreifen zu können. Sie können auch ein anonymes Bitfeld ohne Elementnamen definieren. Solche namenlosen Bitfelder dienen zum Auffüllen eines Bitfeldes, um die Bitzahl auf ein bestimmtes Rechnerwort einzustellen. Mit Breite geben Sie die Anzahl der Bits an, die für das Element verwendet werden sollen. Hierbei muss ein ganzzahliger positiver Ausdruck verwendet werden. Ein Bitfeld mit einer Breite von n kann außerdem 2n verschiedene Werte speichern.

Solche Bitfelder haben außerdem den Vorteil, dass sie wie gewöhnliche Struktur- bzw. Union-Elemente mit dem Elementnamen angesprochen werden.

Bezogen auf das Beispiel unseres Automatisierungsroboters sieht dies folgendermaßen aus:

struct robo {
   unsigned int sensor1:1;
   unsigned int sensor2:1;
   unsigned int sensor3:1;
   unsigned int schalter:1;
   unsigned int Ausgabe:4;
} Roboter1;

Jetzt benötigt die Struktur im Grunde nur noch acht Bits (ein Byte). Wenn Sie allerdings mit sizeof prüfen, werden Sie feststellen, dass dieses Bitfeld trotzdem vier Byte belegt. Dies liegt daran, dass das kleinstmögliche Rechnerwort, das der Compiler für ein Bitfeld reserviert, sizeof(int) groß ist. Die restlichen drei Bytes in diesem Beispiel sind praktisch leer. Gleiches gilt hierbei natürlich auch, wenn Sie beispielsweise ein Bitfeld mit 40 Bits (fünf Bytes) erstellen. Hier muss der Compiler ein weiteres Rechnerwort (sizeof(int)) reservieren, sodass das fünfte Byte am Anfang des nächsten Rechnerworts liegt und somit insgesamt 8 Bytes benötigt werden.


Hinweis

Da Bitfelder keine adressierbare Speicherstelle belegen, können Sie hierbei weder den Adressoperator & noch das Makro offsetof anwenden.


Hier sehen Sie das Beispiel dazu:

/* bitfields.c */
#include <stdio.h>
#include <stdlib.h>

enum{ OFF, ON };

struct robo {
   unsigned int Sensor1:1;
   unsigned int Sensor2:1;
   unsigned int Sensor3:1;
   unsigned int Schalter:1;
   unsigned int Ausgabe:4;
} Roboter1;

char *msg[7] = {
   "Kein Signal an Sensor 1!\n",
   "Kein Signal an Sensor 2!\n",
   "Kein Signal an Sensor 3!\n",
   "Schalter 1 nicht betätigt!\n",
   "Notaus betätigt!\n",
   "Kein Strom!\n"
};

int main(void) {
   int anzahl;

   do {
       printf("Wie viele Produkte von Pos.A nach Pos.B : ");
       do{ scanf("%d",&anzahl); } while(getchar() != '\n');

       while((anzahl>0) && (anzahl--)) {

          /* Sollte durch echte Schnittstelle ersetzt werden. */
          Roboter1.Sensor1=ON;
          printf("Sensor 1 ist aktiv\n");
          if(Roboter1.Sensor1 == ON)
             printf("Produkt wird aufgenommen und "
                    "zum Fließband transportiert\n");
          else {

             /* Fehler: Sensor 1 nicht aktiv
              * Fehlermeldung ausgeben */
             Roboter1.Ausgabe = 0;
             printf("%s\n", msg[Roboter1.Ausgabe]);
          }
            /* Sollte durch echte Schnittstelle ersetzt werden. */
             Roboter1.Sensor2=ON;
             printf("Sensor 2 ist aktiv\n");
             if(Roboter1.Sensor2 == ON) {
                printf("Produkt ist auf dem Fließband\n");
                printf("Bitte >>ENTER<< drücken"
                       " für den Schalter\n");
                getchar();
                printf("Schalter ist eingeschaltet!\n");
                /* Sollte durch echte Schnittstelle
                 * ersetzt werden. */
                Roboter1.Schalter=ON;
             }
             else {
                Roboter1.Ausgabe=1;
                printf("%s\n",msg[Roboter1.Ausgabe]);
                Roboter1.Ausgabe=3;
                printf("%s\n", msg[Roboter1.Ausgabe]);
             }
             /* Sollte durch echte Schnittstelle
              * ersetzt werden. */
             Roboter1.Sensor3=ON;
             printf("Sensor 3 aktiv\n");
             if(Roboter1.Sensor3 == ON) {
                printf("Produkt am Ziel angekommen!\n");
                printf("Schalter für Fließband auf OFF\n");
                printf("Roboter wieder betriebsbereit\n");
                printf("Weiter mit >>ENTER<<\n");
                getchar();
                Roboter1.Schalter=OFF;
             }
             else {
                Roboter1.Ausgabe = 2;
                printf("%s\n", msg[Roboter1.Ausgabe]);
             }
       }
   } while(anzahl > 0);
   Roboter1.Sensor1=OFF;
   Roboter1.Sensor2=OFF;
   Roboter1.Sensor3=OFF;
   Roboter1.Ausgabe=0;
   printf("%s\n",msg[Roboter1.Ausgabe]);
   return EXIT_SUCCESS;
}

Abbildung 15.11 Der Roboter im Einsatz

Dass die Struktur robo hier vier Bytes und nicht ein Byte groß ist, liegt am Alignment des Betriebssystems. Hier kann der Speicherplatz mit dem Keyword attribute oder dem Pragma pack auf ein Byte zusammengepackt werden. Vorausgesetzt, der Compiler unterstützt dies:

//#pragma pack(1)
struct robo {
   unsigned Sensor1:1;
   unsigned Sensor2:1;
   unsigned Sensor3:1;
   unsigned Schalter:1;
   unsigned Ausgabe:4;
} __attribute__ ((packed)) Roboter1;

Abbildung 15.12 zeigt diese Struktur nochmals schematisch.

Abbildung 15.12 Bitbelegung der einzelnen Bitfelder der Struktur »robo«

Das Dumme an diesem Beispiel ist, dass es zu gar nichts taugt, da keine Verbindung mit einem Roboter besteht. Zeilen wie

Roboter1.Sensor1=ON;

Roboter1.Sensor2=ON;

müssen selbst eingegeben werden.

Daher folgt jetzt ein ausführbares Beispiel, das zeigt, was eine Schnittstelle bzw. eine Adresse zum PC genau ist. Es wird der Druckerstatus am Port LPT1 überprüft. Das Listing ist nur unter MS-DOS ausführbar und nicht ANSI-C-konform:

/* check_printer.c */
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
/* 0x378 ist die Adresse der Schnittstelle von LPT1. */
#define LPT1_PORT 0x378

struct status {
   unsigned        :3;     /* Bit 0-2 nicht verwendet */
   unsigned fehler :1;     /* 0=Druckerfehler         */
   unsigned online :1;     /* 1=Drucker online        */
   unsigned papier :1;     /* 1=kein Papier           */
   unsigned empfang:1;     /* Empfangsbestätigung     */
   unsigned busy   :1;     /* Drucker bereit          */
} LPT1_status;

/* Status am LPT1-Port auslesen */
void druckerstatus(char *statuszeiger) {
   *statuszeiger = inp( LPT1_PORT+1 ) & 0xF8;
}

int main(void) {
   druckerstatus( (char *) &LPT1_status);
   if(LPT1_status.busy && LPT1_status.online) {
      printf("Drucker ist bereit!\n");
      return EXIT_SUCCESS;
   }
   else if(!LPT1_status.online)
      printf("Drucker nicht online!\n");
   else if(LPT1_status.papier)
      printf("Kein Papier vorhanden!\n");
   else
      printf("Drucker ist nicht bereit!\n");
   return EXIT_SUCCESS;
}

Die Adresse 0x378 stellt die Adresse des Ports LPT1 dar. Das Statusregister, das hier überprüft wird, sieht so intern aus, wie in Abbildung 15.13 dargestellt.

Diese Struktur ähnelt der Struktur, die oben bei den Robotern verwendet wurde. Die Bits 0–2 werden nicht verwendet. Die Bits 3–7 geben anschließend den Status des Druckers zurück – je nachdem, welche Bits gesetzt sind und welche nicht. Die Funktion druckerstatus() liefert den Status zurück. Neu ist bei diesem Programm:

unsigned:3;

Hiermit werden drei Bits ohne Namen definiert. Im Beispiel sind es die ersten drei Bits, die ungenutzt bleiben. Sie können im Programm nicht verwendet werden und werden als anonyme Bitfelder bezeichnet.

Abbildung 15.13 Bitbelegung eines Druckers am LPT1-Port unter MS-DOS



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: C/C++






 C/C++


Zum Katalog: Einstieg in C






 Einstieg in C


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de