Kapitel – 5
Einführung in die C-Programmierung
Einführung
„C“ ist eine der beliebtesten Programmiersprachen in der modernen Computerwelt. Die Programmiersprache C wurde 1972 von Brian Kernighan und Dennis Ritchie bei Bell Research Labs entwickelt und erstellt.
„C“ ist eine Sprache, die speziell dafür entwickelt wurde, dem Programmierer Zugriff auf fast alle internen Komponenten einer Maschine zu geben – Register, E/A-Steckplätze und absolute Adressen. Gleichzeitig erlaubt „C“ so viel Datenverarbeitung und Modularisierung des programmierten Textes wie nötig, so dass sehr komplexe Multiprogrammierungsprojekte organisiert und zeitsparend erstellt werden können.
Obwohl die Sprache ursprünglich für die Ausführung unter UNIX vorgesehen war, bestand großes Interesse daran, sie auch unter dem Betriebssystem MS-DOS auf dem IBM-PC und kompatiblen Geräten auszuführen. Aufgrund der einfachen Ausdrucksweise, der Kompaktheit des Codes und der breiten Anwendbarkeit ist es eine ausgezeichnete Sprache für diese Umgebung.
Darüber hinaus ist C aufgrund der Einfachheit und Leichtigkeit, mit der sich ein Compiler schreiben lässt, normalerweise die erste höhere Programmiersprache, die auf jedem neuen Computer verfügbar ist, einschließlich Mikrocomputern, Minicomputern und Großrechnern.
Warum C bei der Datenwiederherstellungsprogrammierung verwenden?
In der heutigen Welt der Computerprogrammierung stehen viele höhere Sprachen zur Verfügung. Das Gute an diesen Sprachen ist, dass sie über viele Funktionen verfügen, die für die meisten Programmieraufgaben geeignet sind. Es gibt jedoch mehrere Gründe, warum C die erste Wahl für Programmierer ist, die Datenwiederherstellungsprogrammierung, Systemprogrammierung, Geräteprogrammierung oder Hardwareprogrammierung durchführen möchten:
- C ist eine beliebte und von professionellen Programmierern bevorzugte Sprache. Daher steht eine große Auswahl an C-Compilern und nützlichem Zubehör zur Verfügung.
- C ist eine portable Sprache . Ein für ein Computersystem geschriebenes C-Programm kann mit wenig oder gar keiner Änderung auf einem anderen System kompiliert und ausgeführt werden. Die Portabilität wird durch den ANSI-Standard für C verbessert, einen Regelsatz für C-Compiler.
- C ermöglicht den umfassenden Einsatz von Modulen bei der Programmierung. C-Code kann in Unterroutinen geschrieben werden, die Funktionen genannt werden. Diese Funktionen können in anderen Anwendungen oder Programmen wiederverwendet werden. Sie müssen beim Programmieren einer neuen Anwendung keinen zusätzlichen Aufwand betreiben, um dasselbe Modul zu erstellen, das Sie zuvor in einer anderen Anwendung entwickelt haben.
Sie können diese Funktion im neuen Programm ohne Änderungen oder mit geringfügigen Änderungen verwenden. Bei der Datenwiederherstellungsprogrammierung werden Sie diese Eigenschaft sehr nützlich finden, wenn Sie dieselben Funktionen mehrmals in verschiedenen Anwendungen verschiedener Programme ausführen müssen.
- C ist eine leistungsstarke und flexible Sprache. Aus diesem Grund wird C für so unterschiedliche Projekte wie Betriebssysteme, Textverarbeitungsprogramme, Grafiken, Tabellenkalkulationen und sogar Compiler für andere Sprachen verwendet.
- C ist eine Sprache mit wenigen Wörtern, die nur wenige Begriffe, sogenannte Schlüsselwörter, enthält, die als Grundlage dienen, auf der die Funktionalität der Sprache aufbaut. Diese Schlüsselwörter, auch reservierte Wörter genannt, machen es leistungsfähiger, bieten einen größeren Programmierspielraum und geben dem Programmierer das Gefühl, jede Art von Programmierung in C durchführen zu können.
Lassen Sie mich davon ausgehen, dass Sie nichts über C wissen.
Ich gehe davon aus, dass Du keine Ahnung von C-Programmierung hast und keine Ahnung vom Programmieren hast. Ich beginne mit den grundlegendsten Konzepten der Sprache C und führe Sie bis zur C-Programmierung auf höherer Ebene, einschließlich der normalerweise einschüchternden Konzepte von Zeigern, Strukturen und dynamischer Speicherzuweisung.
Das vollständige Verständnis dieser Konzepte wird viel Zeit und Mühe kosten, da sie nicht leicht zu verstehen sind, es sich dabei jedoch um sehr mächtige Werkzeuge handelt.
Die C-Programmierung ist in Bereichen von großem Vorteil, in denen Sie Assemblersprache benötigen, diese aber lieber einfach zu schreiben und zu verwalten halten möchten. Die Zeitersparnis durch die Codierung in C kann in solchen Fällen enorm sein.
Auch wenn die Sprache C beim Übertragen von Programmen von einer Implementierung auf eine andere eine gute Erfolgsbilanz aufweist, werden Sie bei jedem Versuch, einen anderen Compiler zu verwenden, Unterschiede bei den Compilern feststellen.
Die meisten Unterschiede werden deutlich, wenn Sie nicht standardmäßige Erweiterungen verwenden, wie z. B. Aufrufe des DOS-BIOS bei der Verwendung von MS-DOS. Aber selbst diese Unterschiede können durch eine sorgfältige Auswahl der Programmierkonstrukte minimiert werden.
Als deutlich wurde, dass die Programmiersprache C immer beliebter wurde und auf vielen Computern verfügbar war, traf sich eine Gruppe interessierter Personen, um einen Standardsatz an Regeln für die Verwendung der Programmiersprache C vorzuschlagen.
Die Gruppe repräsentierte alle Bereiche der Softwareindustrie und verfasste nach zahlreichen Sitzungen und vorläufigen Entwürfen schließlich einen akzeptablen Standard für die Sprache C. Dieser wurde vom American National Standards Institute (ANSI) und der International Standards Organization (ISO) angenommen .
Er wird keiner Gruppe oder keinem Benutzer aufgezwungen, aber da er eine so hohe Akzeptanz genießt, wäre es für jeden Compiler-Autor wirtschaftlicher Selbstmord, sich zu weigern, den Standard einzuhalten.
Die in diesem Buch geschriebenen Programme sind in erster Linie für die Verwendung auf einem IBM-PC oder kompatiblen Computer gedacht, können jedoch mit jedem ANSI-Standardcompiler verwendet werden, da sie dem ANSI-Standard sehr genau entsprechen.
Lasst uns beginnen
Bevor Sie in einer beliebigen Sprache etwas tun und mit dem Programmieren beginnen können, müssen Sie wissen, wie Sie einen Bezeichner benennen. Ein Bezeichner wird für jede Variable, Funktion, Datendefinition usw. verwendet. In der Programmiersprache C ist ein Bezeichner eine Kombination aus alphanumerischen Zeichen, wobei das erste Zeichen ein Buchstabe des Alphabets oder ein Unterstrich ist und die restlichen Zeichen ein beliebiger Buchstabe des Alphabets, eine beliebige Ziffer oder der Unterstrich.
Bei der Benennung von Bezeichnern müssen zwei Regeln beachtet werden.
- Die Groß- und Kleinschreibung von Buchstaben ist wichtig. C ist eine Groß- und Kleinschreibungssensitiv-Sprache. Das bedeutet, dass Recovery nicht dasselbe ist wie recovery und rEcOveRY nicht dasselbe ist wie die beiden zuvor genannten.
- Gemäß dem ANSI-C-Standard können mindestens 31 signifikante Zeichen verwendet werden und werden von einem konformen ANSI-C-Compiler als signifikant betrachtet. Wenn mehr als 31 verwendet werden, können alle Zeichen nach dem 31. von einem beliebigen Compiler ignoriert werden.
Schlagwörter
In C sind 32 Wörter als Schlüsselwörter definiert. Diese haben vordefinierte Verwendungszwecke und können in einem C-Programm nicht für andere Zwecke verwendet werden. Sie werden vom Compiler als Hilfe zum Kompilieren des Programms verwendet. Sie werden immer in Kleinbuchstaben geschrieben. Es folgt eine vollständige Liste:
Auto |
brechen |
Fall |
verkohlen |
Konstante |
weitermachen |
Standard |
Tun |
doppelt |
anders |
Aufzählung |
extern |
schweben |
für |
gehe zu |
Wenn |
int |
lang |
registrieren |
zurückkehren |
kurz |
unterzeichnet |
Größevon |
statisch |
Struktur |
schalten |
Typdefinition |
Union |
ohne Vorzeichen |
Leere |
flüchtig |
während |
Hier sehen wir die Magie von C. Die wunderbare Sammlung von nur 32 Schlüsselwörtern bietet eine breite Verwendung in verschiedenen Anwendungen. Jedes Computerprogramm muss zwei Entitäten berücksichtigen: die Daten und das Programm. Sie sind stark voneinander abhängig und eine sorgfältige Planung beider führt zu einem gut geplanten und gut geschriebenen Programm.
Beginnen wir mit einem einfachen C-Programm:
/* Erstes Programm zum Erlernen von C */
#include <stdio.h>
void main()
{
printf("Dies ist ein C-Programm\n"); // eine Nachricht drucken
}
Obwohl das Programm sehr einfach ist, sind einige Punkte bemerkenswert. Sehen wir uns das obige Programm genauer an. Alles, was zwischen /* und */ steht, wird als Kommentar betrachtet und vom Compiler ignoriert. Sie sollten keine Kommentare in andere Kommentare einfügen, daher ist so etwas nicht erlaubt:
/* dies ist ein /* Kommentar */ innerhalb eines Kommentars, was falsch ist */
Es gibt auch eine Dokumentationsmethode, die innerhalb einer Zeile funktioniert. Durch die Verwendung von // können wir innerhalb dieser Zeile eine kurze Dokumentation hinzufügen.
Jedes C-Programm enthält eine Funktion namens main. Dies ist der Startpunkt des Programms. Jede Funktion sollte einen Wert zurückgeben. In diesem Programm gibt die Funktion main keinen Rückgabewert zurück, daher haben wir void main geschrieben. Wir könnten dieses Programm auch so schreiben:
/* Erstes Programm zum Erlernen von C */
#include <stdio.h>
hauptsächlich()
{
printf("Dies ist ein C-Programm\n"); // eine Nachricht drucken
gebe 0 zurück;
}
Beide Programme sind gleich und führen dieselbe Aufgabe aus. Das Ergebnis beider Programme wird die folgende Ausgabe auf dem Bildschirm ausgeben:
Dies ist ein C-Programm
#include<stdio.h> ermöglicht dem Programm die Interaktion mit dem Bildschirm, der Tastatur und dem Dateisystem Ihres Computers. Sie finden es am Anfang fast jedes C-Programms.
main() deklariert den Beginn der Funktion, während die beiden geschweiften Klammern den Anfang und das Ende der Funktion anzeigen. Geschweifte Klammern werden in C verwendet, um Anweisungen wie in einer Funktion oder im Hauptteil einer Schleife zu gruppieren. Eine solche Gruppierung wird als zusammengesetzte Anweisung oder Block bezeichnet.
printf("Dies ist ein C-Programm\n"); druckt die Wörter auf dem Bildschirm. Der auszudruckende Text wird in doppelte Anführungszeichen gesetzt. Das \n am Ende des Textes weist das Programm an, als Teil der Ausgabe eine neue Zeile auszudrucken. Die Funktion printf() wird zur Anzeige der Ausgabe auf dem Monitor verwendet.
Die meisten C-Programme sind in Kleinbuchstaben geschrieben. Großbuchstaben finden Sie normalerweise in Präprozessordefinitionen, die später besprochen werden, oder in Anführungszeichen als Teil von Zeichenfolgen.
Kompilieren des Programms
Der Name unseres Programms sei CPROG.C. Um das C-Programm einzugeben und zu kompilieren, folgen Sie diesen Schritten:
- Make the active directory of your C programs and start your editor. For this any text editor can be used, but most C compilers such as Borland's Turbo C++ has an integrated development environment (IDE) that lets you enter, compile, and link your programs in one convenient setting.
- Write and save the source code. You should name the file CPROG.C.
- Compile and link CPROG.C. Execute the appropriate command specified by your compiler's manuals. You should get a message stating that there were no errors or warnings.
- Check the compiler messages. If you receive no errors or warnings, everything should be okay. If there is any error in typing the program, the compiler will catch it and display an error message. Correct the error, displayed in the error message.
- Your first C program should now be compiled and ready to run. If you display a directory listing of all files named CPROG you will get the four files with different extension described as follows:
- CPROG.C, the source code file
- CPROG.BAK, the backup file of source file you created with editor
- CPROG.OBJ, contains the object code for CPROG.C
- CPROG.EXE, the executable program created when you compiled and linked CPROG.C
- To execute, or run, CPROG.EXE, simply enter cprog. The message, This is a C program is displayed on-screen.
Now let us examine the following program:
/* First Program to learn C */ // 1
// 2
#include <stdio.h> // 3
// 4
main() // 5
{
// 6
printf("This is a C program\n"); // 7
// 8
return 0; // 9
} // 10
When you compile this program, the compiler displays a message similar to the following:
cprog.c(8) : Error: `;' expected
let us break this error message in parts. cprog.c is the name of the file where the error was found. (8) is line number where the error was found. Error: `;' expected is A description of the error.
This message is quite informative, and tells you that in line 8 of CPROG.C the compiler expected to find a semicolon but didn't. However, you know that the semicolon was actually omitted from line 7, so there is a discrepancy.
Why the compiler reports an error in line 8 when, in fact, a semicolon was omitted from line 7. The answer lies in the fact that C doesn't care about things like breaks between lines. The semicolon that belongs after the printf() statement could have been placed on the next line though doing so would be bad programming in practice.
Only after encountering the next command (return) in line 8 is the compiler sure that the semicolon is missing. Therefore, the compiler reports that the error is in line 8.
Es gibt eine Reihe von Möglichkeiten für verschiedene Fehlertypen. Lassen Sie uns über Fehlermeldungen beim Verknüpfen sprechen. Linker-Fehler sind relativ selten und resultieren normalerweise aus einem falsch geschriebenen Namen einer C-Bibliotheksfunktion. In diesem Fall erhalten Sie die Fehlermeldung „Error: undefined symbols:“, gefolgt vom falsch geschriebenen Namen. Sobald Sie die Schreibweise korrigiert haben, sollte das Problem behoben sein.
Zahlen drucken
Sehen wir uns das folgende Beispiel an:
// So drucken Sie die Zahlen //
#include<stdio.h>
void main()
{
int num = 10;
printf("Die Zahl ist %d", num);
}
Die Ausgabe des Programms wird wie folgt auf dem Bildschirm angezeigt:
Die Zahl ist 10
Das %-Zeichen wird verwendet, um die Ausgabe vieler verschiedener Variablentypen zu signalisieren. Das Zeichen nach dem %-Zeichen ist ad, das der Ausgaberoutine signalisiert, einen Dezimalwert abzurufen und auszugeben.
Verwenden von Variablen
In C muss eine Variable deklariert werden, bevor sie verwendet werden kann. Variablen können am Anfang jedes Codeblocks deklariert werden, die meisten stehen jedoch am Anfang jeder Funktion. Die meisten lokalen Variablen werden beim Aufruf der Funktion erstellt und bei der Rückkehr von dieser Funktion zerstört.
Um Variablen in Ihren C-Programmen verwenden zu können, müssen Sie die folgenden Regeln für die Benennung von Variablen in C kennen:
- Der Name kann Buchstaben, Ziffern und das Unterstrichzeichen (_) enthalten.
- Das erste Zeichen des Namens muss ein Buchstabe sein. Der Unterstrich ist ebenfalls ein zulässiges erstes Zeichen, seine Verwendung wird jedoch nicht empfohlen.
- C unterscheidet zwischen Groß- und Kleinschreibung, daher ist der Variablenname num etwas anderes als Num.
- C-Schlüsselwörter können nicht als Variablennamen verwendet werden. Ein Schlüsselwort ist ein Wort, das Teil der Sprache C ist.
Die folgende Liste enthält einige Beispiele für zulässige und unzulässige C-Variablennamen:
Variablenname |
Legal oder nicht |
In einem |
Rechtliches |
Ttpt2_t2p |
Rechtliches |
Tt pt |
Illegal: Leerzeichen sind nicht erlaubt |
_1990_Steuer |
Legal, aber nicht ratsam |
Jack_phone# |
Unzulässig: Enthält das unzulässige Zeichen # |
Fall |
Unzulässig: Ist ein C-Schlüsselwort |
1Buch |
Unzulässig: Das erste Zeichen ist eine Ziffer |
Das erste Neue, das auffällt, ist die erste Zeile des Hauptteils von main():
int num = 10;
Diese Zeile definiert eine Variable namens „num“ vom Typ int und initialisiert sie mit dem Wert 10. Dies hätte auch so geschrieben werden können:
int num; /* nicht initialisierte Variable „num“ definieren */
/* und nach allen Variablendefinitionen: */
num = 10; /* weist der Variablen „num“ den Wert 10 zu */
Variablen können am Anfang eines Blocks (zwischen den Klammern {und}) definiert werden, normalerweise ist dies am Anfang eines Funktionskörpers der Fall, es kann aber auch am Anfang eines anderen Blocktyps stehen.
Variablen, die am Anfang eines Blocks definiert werden, haben standardmäßig den Status „auto“. Das bedeutet, dass sie nur während der Ausführung des Blocks existieren. Wenn die Funktionsausführung beginnt, werden die Variablen erstellt, aber ihr Inhalt ist undefiniert. Wenn die Funktion zurückkehrt, werden die Variablen zerstört. Die Definition hätte auch wie folgt geschrieben werden können:
auto int num = 10;
Da die Definition mit oder ohne das Schlüsselwort „auto“ völlig gleichwertig ist, ist das Schlüsselwort „auto“ offensichtlich ziemlich redundant.
Manchmal ist das jedoch nicht das, was Sie möchten. Angenommen, Sie möchten, dass eine Funktion zählt, wie oft sie aufgerufen wird. Wenn die Variable bei jeder Rückkehr der Funktion zerstört würde, wäre dies nicht möglich.
Daher ist es möglich, der Variable eine sogenannte statische Dauer zuzuweisen, was bedeutet, dass sie während der gesamten Ausführung des Programms unverändert bleibt. Zum Beispiel:
statische int-Nummer = 10;
Dadurch wird die Variable num zu Beginn der Programmausführung auf 10 initialisiert. Von da an bleibt der Wert unverändert; die Variable wird nicht neu initialisiert, wenn die Funktion mehrmals aufgerufen wird.
Manchmal reicht es nicht aus, dass auf die Variable nur von einer Funktion aus zugegriffen werden kann, oder es ist möglicherweise nicht praktisch, den Wert über einen Parameter an alle anderen Funktionen zu übergeben, die ihn benötigen.
Wenn Sie jedoch von allen Funktionen in der gesamten Quelldatei aus auf die Variable zugreifen müssen, können Sie dies auch mit dem Schlüsselwort static tun, indem Sie die Definition jedoch außerhalb aller Funktionen platzieren. Beispiel:
#include <stdio.h>
static int num = 10; /* wird von der gesamten Quelldatei aus zugänglich sein */
int Haupt(void)
{
printf("Die Zahl ist: %d\n", num);
gebe 0 zurück;
}
Und es gibt auch Fälle, in denen eine Variable vom gesamten Programm aus zugänglich sein muss, das aus mehreren Quelldateien bestehen kann. Dies wird als globale Variable bezeichnet und sollte vermieden werden, wenn es nicht erforderlich ist.
Dies wird auch dadurch erreicht, dass die Definition außerhalb aller Funktionen platziert wird, jedoch ohne das Schlüsselwort static zu verwenden:
#include <stdio.h>
int num = 10; /* wird vom gesamten Programm aus zugänglich sein! */
int Haupt(void)
{
printf("Die Zahl ist: %d\n", num);
gebe 0 zurück;
}
Es gibt auch das Schlüsselwort extern, das für den Zugriff auf globale Variablen in anderen Modulen verwendet wird. Es gibt auch einige Qualifizierer, die Sie zu Variablendefinitionen hinzufügen können. Der wichtigste davon ist const. Eine Variable, die als const definiert ist, darf nicht geändert werden.
Es gibt noch zwei weitere Modifikatoren, die weniger häufig verwendet werden. Der volatile und der Registermodifikator. Der volatile Modifikator erfordert, dass der Compiler bei jedem Lesen tatsächlich auf die Variable zugreift. Er kann die Variable nicht optimieren, indem er sie in ein Register oder Ähnliches einfügt. Dies wird hauptsächlich für Multithreading und Interruptverarbeitungszwecke usw. verwendet.
Der Registermodifikator fordert den Compiler auf, die Variable in ein Register zu optimieren. Dies ist nur mit automatischen Variablen möglich und in vielen Fällen kann der Compiler die in Register zu optimierenden Variablen besser auswählen, sodass dieses Schlüsselwort veraltet ist. Die einzige direkte Konsequenz der Erstellung eines Variablenregisters ist, dass seine Adresse nicht übernommen werden kann.
Die Variablentabelle auf der nächsten Seite beschreibt die Speicherklasse von fünf Speicherklassentypen.
In der Tabelle sehen wir, dass das Schlüsselwort extern in zwei Zeilen steht. Das Schlüsselwort extern wird in Funktionen verwendet, um eine statische externe Variable zu deklarieren, die an anderer Stelle definiert ist.
Numerische Variablentypen
C bietet mehrere verschiedene Typen numerischer Variablen, da verschiedene numerische Werte unterschiedliche Speicheranforderungen haben. Diese numerischen Typen unterscheiden sich in der Leichtigkeit, mit der bestimmte mathematische Operationen an ihnen durchgeführt werden können.
Kleine Ganzzahlen benötigen weniger Speicherplatz und Ihr Computer kann mathematische Operationen mit solchen Zahlen sehr schnell durchführen. Große Ganzzahlen und Fließkommawerte benötigen mehr Speicherplatz und mehr Zeit für mathematische Operationen. Durch die Verwendung der entsprechenden Variablentypen stellen Sie sicher, dass Ihr Programm so effizient wie möglich ausgeführt wird.
Die numerischen Variablen von C fallen in die folgenden zwei Hauptkategorien:
- Integer-Variablen
- Gleitkommavariablen
Innerhalb jeder dieser Kategorien gibt es zwei oder mehr spezifische Variablentypen. Die folgende Tabelle zeigt die Speichermenge in Bytes, die zum Speichern einer einzelnen Variable jedes Typs erforderlich ist.
Der Typ char kann entweder mit signed char oder unsigned char gleichwertig sein, es handelt sich jedoch immer um einen von diesen getrennten Typ.
In C gibt es keinen Unterschied zwischen der Speicherung von Zeichen oder den entsprechenden numerischen Werten in einer Variablen. Daher ist auch keine Funktion erforderlich, um zwischen einem Zeichen und seinem numerischen Wert oder umgekehrt zu konvertieren. Wenn Sie für die anderen Integer-Typen „signed“ oder „unsigned“ weglassen, wird standardmäßig „signed“ verwendet. Int und „signed int“ sind also beispielsweise gleichwertig.
Der Typ int muss größer oder gleich dem Typ short und kleiner oder gleich dem Typ long sein. Wenn Sie einfach einige Werte speichern müssen, die nicht enorm groß sind, ist es oft eine gute Idee, den Typ int zu verwenden; dies ist normalerweise die Größe, mit der der Prozessor am einfachsten und daher am schnellsten umgehen kann.
Bei einigen Compilern sind double und long double gleichwertig. Dies und die Tatsache, dass die meisten Standardfunktionen der Mathematik mit dem Typ double arbeiten, ist ein guter Grund, immer den Typ double zu verwenden, wenn Sie mit Bruchzahlen arbeiten müssen.
Zur besseren Beschreibung der Variablentypen dient folgende Tabelle:
Häufig verwendete Spezialtypen:
Variablentyp |
Beschreibung |
Größe_t |
vorzeichenloser Typ zum Speichern der Objektgrößen in Bytes |
Zeit_t |
wird zum Speichern der Ergebnisse der Funktion time() verwendet |
clock_t |
wird zum Speichern der Ergebnisse der Funktion clock() verwendet |
DATEI |
wird für den Zugriff auf einen Stream (normalerweise eine Datei oder ein Gerät) verwendet |
ptrdiff_t |
vorzeichenbehafteter Typ der Differenz zwischen 2 Zeigern |
div_t |
wird zum Speichern der Ergebnisse der div()-Funktion verwendet |
ldiv_t |
wird zum Speichern der Ergebnisse der Funktion ldiv() verwendet |
fpos_t |
Wird verwendet, um Informationen zur Dateiposition zu speichern |
will_list |
wird bei der Behandlung variabler Argumente verwendet |
wchar_t |
breiter Zeichentyp (für erweiterte Zeichensätze verwendet) |
sig_atomic_t |
wird in Signalhandlern verwendet |
Jmp_buf |
wird für nicht lokale Sprünge verwendet |
Um diese Variablen besser zu verstehen, betrachten wir ein Beispiel:
/* Programm zum Angeben des Bereichs und der Größe der C-Variable in Bytes */
#include <stdio.h>
int main()
{
int a; /* einfacher Integer-Typ */
long int b; /* langer Integer-Typ */
short int c; /* kurzer Integer-Typ */
unsigned int d; /* vorzeichenloser Integer-Typ */
char e; /* Zeichentyp */
float f; /* Gleitkommatyp */
double g; /* Gleitkommazahl mit doppelter Genauigkeit */
ein = 1023;
b = 2222;
c = 123;
d = 1234;
e = "X";
f = 3,14159;
g = 3,1415926535898;
printf( "\nEin Zeichen ist %d Bytes lang", sizeof( char ));
printf( "\nEin int ist %d Bytes groß", sizeof( int ));
printf( "\nEin Short ist %d Bytes", sizeof( short ));
printf( "\nEin long ist %d Bytes", sizeof( long ));
printf( "\nEin vorzeichenloses Zeichen ist %d Bytes lang",
Größe von (unsigned char));
printf( "\nEin vorzeichenloser int ist %d Bytes groß",
Größe von (vorzeichenlose Ganzzahl));
printf( "\nEin vorzeichenloser Short ist %d Bytes lang",
Größe von (unsigned short));
printf( "\nEin vorzeichenloser Long-Wert ist %d Bytes lang",
Größe von (vorzeichenlosem Long));
printf( "\nEine Float-Zahl ist %d Byte groß", sizeof( float ));
printf( "\nEin Double ist %d Bytes\n", sizeof( double ));
printf("a = %d\n", a); /* Dezimalausgabe */
printf("a = %o\n", a); /* Oktalausgabe */
printf("a = %x\n", a); /* hexadezimale Ausgabe */
printf("b = %ld\n", b); /* dezimale lange Ausgabe */
printf("c = %d\n", c); /* dezimale Kurzausgabe */
printf("d = %u\n", d); /* vorzeichenlose Ausgabe */
printf("e = %c\n", e); /* Zeichenausgabe */
printf("f = %f\n", f); /* Fließkomma-Ausgabe */
printf("g = %f\n", g); /* doppelte Float-Ausgabe */
printf("\n");
printf("a = %d\n", a); /* einfache int-Ausgabe */
printf("a = %7d\n", a); /* verwende eine Feldbreite von 7 */
printf("a = %-7d\n", a); /* linksbündig ausrichten in
Feld von 7 */
c = 5;
d = 8;
printf("a = %*d\n", c, a); /* verwende eine Feldbreite von 5*/
printf("a = %*d\n", d, a); /* verwende eine Feldbreite von 8 */
printf("\n");
printf("f = %f\n", f); /* einfache Float-Ausgabe */
printf("f = %12f\n", f); /* Feldbreite von 12 verwenden */
printf("f = %12.3f\n", f); /* 3 Dezimalstellen verwenden */
printf("f = %12.5f\n", f); /* 5 Dezimalstellen verwenden */
printf("f = %-12.5f\n", f); /* im Feld linksbündig ausrichten */
gebe 0 zurück;
}
Das Ergebnis des Programms wird nach der Ausführung wie folgt angezeigt:
Ein Zeichen ist 1 Byte
Ein int ist 2 Bytes
Ein Short hat 2 Bytes
Ein Long ist 4 Bytes lang
Ein vorzeichenloses Zeichen ist 1 Byte groß
Ein vorzeichenloser int ist 2 Bytes
Ein vorzeichenloser Short ist 2 Bytes lang
Ein unsigned long ist 4 Bytes
Ein Float ist 4 Bytes groß
Ein Double hat 8 Bytes
ein = 1023
ein = 1777
a = 3ff
b = 2222
c = 123
d = 1234
e = X
f = 3,141590
g = 3,141593
ein = 1023
ein = 1023
ein = 1023
ein = 1023
ein = 1023
f = 3,141590
f = 3,141590
f = 3,142
f = 3,14159
f = 3,14159 |
Bevor eine Variable in einem C-Programm verwendet werden kann, muss sie deklariert werden. Eine Variablendeklaration teilt dem Compiler den Namen und Typ einer Variablen mit und initialisiert die Variable optional auf einen bestimmten Wert.
Wenn Ihr Programm versucht, eine Variable zu verwenden, die nicht deklariert wurde, generiert der Compiler eine Fehlermeldung. Eine Variablendeklaration hat folgende Form:
Typname Varname;
typename gibt den Variablentyp an und muss eines der Schlüsselwörter sein. varname ist der Variablenname. Sie können mehrere Variablen desselben Typs in einer Zeile deklarieren, indem Sie die Variablennamen durch Kommas trennen:
int count, number, start; /* drei Integer-Variablen */
Float-Prozent, Gesamtsumme; /* zwei Float-Variablen */
Das Schlüsselwort typedef
Das Schlüsselwort typedef wird verwendet, um einen neuen Namen für einen vorhandenen Datentyp zu erstellen. Tatsächlich erstellt typedef ein Synonym. Beispielsweise die Anweisung
Typdefinition int Ganzzahl;
Hier sehen wir, dass typedef integer als Synonym für int erstellt. Sie können integer dann verwenden, um Variablen vom Typ int zu definieren, wie in diesem Beispiel:
Ganzzahlanzahl;
Daher erstellt typedef keinen neuen Datentyp, sondern ermöglicht Ihnen lediglich, für einen vordefinierten Datentyp einen anderen Namen zu verwenden.
Initialisieren numerischer Variablen
Wenn eine Variable deklariert wird, wird der Compiler angewiesen, Speicherplatz für die Variable freizugeben. Der in diesem Speicherplatz gespeicherte Wert, der Wert der Variable, ist jedoch nicht definiert. Er könnte Null sein oder ein beliebiger „Müll“-Wert. Bevor Sie eine Variable verwenden, sollten Sie sie immer auf einen bekannten Wert initialisieren. Nehmen wir dieses Beispiel:
int count; /* Speicherplatz für count reservieren */
Anzahl = 0; /* 0 in Anzahl speichern */
Diese Anweisung verwendet das Gleichheitszeichen (=), den Zuweisungsoperator von C. Sie können eine Variable auch bei ihrer Deklaration initialisieren. Dazu müssen Sie in der Deklarationsanweisung dem Variablennamen ein Gleichheitszeichen und den gewünschten Anfangswert hinzufügen:
int-Anzahl = 0;
Doppelrate = 0,01, Komplexität = 28,5;
Achten Sie darauf, eine Variable nicht mit einem Wert außerhalb des zulässigen Bereichs zu initialisieren. Hier sind zwei Beispiele für Initialisierungen außerhalb des zulässigen Bereichs:
int-Betrag = 100000;
vorzeichenlose Ganzzahllänge = -2500;
Der C-Compiler erkennt solche Fehler nicht. Ihr Programm kann kompiliert und verknüpft werden, aber beim Ausführen des Programms können unerwartete Ergebnisse auftreten.
Betrachten wir das folgende Beispiel zur Berechnung der Gesamtzahl der Sektoren auf einer Festplatte:
// Modellprogramm zum Berechnen von Sektoren auf einer Festplatte //
#include<stdio.h>
#define SEKTOR_PRO_SEITE 63
#define SEITE_PRO_ZYLINDER 254
void main()
{
int Zylinder=0;
clrscr();
printf("Geben Sie die Anzahl der Zylinder auf der Festplatte ein \n\n\t");
scanf("%d",&cylinder); // Den Wert vom Benutzer abrufen //
printf("\n\n\t Gesamtzahl der Sektoren auf der Festplatte = %ld", (long)SECTOR_PER_SIDE*SIDE_PER_CYLINDER* Zylinder);
getch();
}
Die Ausgabe des Programms lautet wie folgt:
Geben Sie die Anzahl der Zylinder in der Festplatte ein
1024
Gesamtzahl der Sektoren auf der Festplatte = 16386048
In diesem Beispiel können wir drei neue Dinge lernen. #define wird verwendet, um symbolische Konstanten im Programm zu verwenden oder in einigen Fällen Zeit zu sparen, indem lange Wörter in kleinen Symbolen definiert werden.
Hier haben wir die Anzahl der Sektoren pro Seite, also 63, als SECTOR_PER_SIDE definiert, um das Programm leichter verständlich zu machen. Dasselbe gilt für #define SIDE_PER_CYLINDER 254. scanf() wird verwendet, um die Eingabe vom Benutzer abzurufen.
Hier nehmen wir die Anzahl der Zylinder als Eingabe vom Benutzer. * wird verwendet, um zwei oder mehr Werte zu multiplizieren, wie im Beispiel gezeigt.
Die Funktion getch() ruft grundsätzlich eine einzelne Zeicheneingabe über die Tastatur ab. Durch die Eingabe von getch() stoppen wir hier den Bildschirm, bis eine beliebige Taste auf der Tastatur gedrückt wird.
Betreiber
Ein Operator ist ein Symbol, das C anweist, eine Operation oder Aktion an einem oder mehreren Operanden auszuführen. Ein Operand ist etwas, auf das ein Operator einwirkt. In C sind alle Operanden Ausdrücke. C-Operatoren lassen sich in die folgenden vier Kategorien einteilen:
- Der Zuweisungsoperator
- Mathematische Operatoren
- Relationale Operatoren
- Logische Operatoren
Zuweisungsoperator
Der Zuweisungsoperator ist das Gleichheitszeichen (=). Die Verwendung des Gleichheitszeichens in der Programmierung unterscheidet sich von der Verwendung in regulären mathematischen algebraischen Beziehungen. Wenn Sie schreiben
x = y;
In einem C-Programm bedeutet es nicht „x ist gleich y“. Stattdessen bedeutet es „weise x den Wert von y zu“. In einer C-Zuweisungsanweisung kann die rechte Seite ein beliebiger Ausdruck sein und die linke Seite muss ein Variablenname sein. Die Form sieht also wie folgt aus:
Variable = Ausdruck;
Während der Ausführung wird der Ausdruck ausgewertet und der resultierende Wert der Variablen zugewiesen.
Mathematische Operatoren
Die mathematischen Operatoren von C führen mathematische Operationen wie Addition und Subtraktion aus. C hat zwei unäre mathematische Operatoren und fünf binäre mathematische Operatoren. Die unären mathematischen Operatoren werden so genannt, weil sie einen einzelnen Operanden annehmen. C hat zwei unäre mathematische Operatoren.
Die Inkrement- und Dekrementoperatoren können nur mit Variablen, nicht mit Konstanten verwendet werden. Die ausgeführte Operation besteht darin, eins zum Operanden zu addieren oder eins davon zu subtrahieren. Mit anderen Worten sind die Anweisungen ++x; und --y; die Äquivalente dieser Anweisungen:
x = x + 1;
y = y - 1;
Binäre mathematische Operatoren nehmen zwei Operanden. Die ersten vier binären Operatoren, zu denen die üblichen mathematischen Operationen auf einem Taschenrechner (+, -, *, /) gehören, sind Ihnen bekannt. Der fünfte Operator Modulus gibt den Rest zurück, wenn der erste Operand durch den zweiten Operanden geteilt wird. Beispiel: 11 modulus 4 ergibt 3 (11 wird zweimal durch 4 geteilt und es bleiben 3 übrig).
Relationale Operatoren
Die relationalen Operatoren von C werden zum Vergleichen von Ausdrücken verwendet. Ein Ausdruck, der einen relationalen Operator enthält, wird entweder als wahr (1) oder falsch (0) ausgewertet. C hat sechs relationale Operatoren.
Logische Operatoren
Mit den logischen Operatoren von C können Sie zwei oder mehr relationale Ausdrücke zu einem einzigen Ausdruck kombinieren, der entweder wahr oder falsch ausgewertet wird. Logische Operatoren werden entweder wahr oder falsch ausgewertet, abhängig vom wahren oder falschen Wert ihrer Operanden.
Wenn x eine ganzzahlige Variable ist, können Ausdrücke mit logischen Operatoren folgendermaßen geschrieben werden:
(x > 1) && (x < 5)
(x >= 2) && (x <= 4)
Operator |
Symbol |
Beschreibung |
Beispiel |
Zuweisungsoperatoren |
gleich |
= |
Weisen Sie x den Wert von y zu |
x = y |
Mathematische Operatoren |
Inkrement |
++ |
Erhöht den Operanden um eins |
++x, x++ |
Dekrement |
-- |
Dekrementiert den Operanden um eins |
--x, x-- |
Zusatz |
+ |
Addiert zwei Operanden |
x + y |
Subtraktion |
- |
Subtrahiert den zweiten Operanden vom ersten |
x - y |
Multiplikation |
* |
Multipliziert zwei Operanden |
x * y |
Division |
/ |
Dividiert den ersten Operanden durch den zweiten Operanden |
x / y |
Modul |
% |
Gibt den Rest zurück, wenn der erste Operand durch den zweiten Operanden geteilt wird |
x % und |
Relationale Operatoren |
Gleich |
= = |
Gleichwertigkeit |
x = = y |
Größer als |
> |
Größer als |
x > y |
Weniger als |
< |
Weniger als |
x < y |
Größer als oder gleich |
>= |
Größer als oder gleich |
x >= y |
Kleiner oder gleich |
<= |
Kleiner oder gleich |
x <= y |
Ungleich |
!= |
Ungleich |
x != y |
Logische Operatoren |
UND |
&& |
Nur wahr (1), wenn exp1 und exp2 beide wahr sind, andernfalls falsch (0). |
exp1 && exp2 |
ODER |
|| |
Wahr (1), wenn entweder exp1 oder exp2 wahr ist; falsch (0), nur wenn beide falsch sind. |
exp1 || exp2 |
NICHT |
! |
Falsch (0), wenn exp1 wahr ist; wahr (1), wenn exp1 falsch ist |
!exp1 |
Was Sie über logische Ausdrücke wissen sollten
x * = y |
ist dasselbe wie |
x = x * y |
y - = z + 1 |
ist dasselbe wie |
y = y - z + 1 |
ein / = b |
ist dasselbe wie |
a = a / b |
x + = y / 8 |
ist dasselbe wie |
x = x + y / 8 |
und % = 3 |
ist dasselbe wie |
y = y % 3 |
Der Komma-Operator
Das Komma wird in C häufig als einfaches Satzzeichen verwendet, um Variablendeklarationen, Funktionsargumente usw. zu trennen. In bestimmten Situationen fungiert das Komma als Operator.
Sie können einen Ausdruck bilden, indem Sie zwei Teilausdrücke durch Kommas trennen. Das Ergebnis ist wie folgt:
- Beide Ausdrücke werden ausgewertet, wobei der linke Ausdruck zuerst ausgewertet wird.
- Der gesamte Ausdruck ergibt den Wert des rechten Ausdrucks.
Beispielsweise weist die folgende Anweisung x den Wert von b zu, erhöht dann a und dann b: x = (a++, b++);
C-Operator-Priorität (Zusammenfassung der C-Operatoren)
Rang und Assoziativität |
Betreiber |
1 (von links nach rechts) |
() [] -> . |
2 (von rechts nach links) |
! ~ ++ -- * (Indirektion) & (Adresse von) (Typ) sizeof + (unär) - (unär) |
3 (von links nach rechts) |
* (Multiplikation) / % |
4 (von links nach rechts) |
+ - |
5 (von links nach rechts) |
<< >> |
6 (von links nach rechts) |
< <= > >= |
7 (von links nach rechts) |
= = != |
8 (von links nach rechts) |
& (bitweises UND ) |
9 (von links nach rechts) |
^ |
10 (von links nach rechts) |
| |
11 (von links nach rechts) |
&& |
12 (von links nach rechts) |
|| |
13 (von rechts nach links) |
?: |
14 (von rechts nach links) |
= += -= *= /= %= &= ^= |= <<= >>= |
15 (von links nach rechts) |
, |
() ist der Funktionsoperator; [] ist der Array-Operator. |
|
Betrachten wir die Verwendung von Operatoren anhand eines Beispiels:
/* Verwendung von Operatoren */
int main()
{
int x = 0, y = 2, z = 1025;
Gleitkommazahl a = 0,0, b = 3,14159, c = -37,234;
/* Inkrementieren */
x = x + 1; /* Dies erhöht x */
x++; /* Dies erhöht x */
++x; /* Dies erhöht x */
z = y++; /* z = 2, y = 3 */
z = ++y; /* z = 4, y = 4 */
/* Dekrementieren */
y = y - 1; /* Dies dekrementiert y */
y--; /* Dies dekrementiert y */
--y; /* Dies dekrementiert y */
y = 3;
z = y--; /* z = 3, y = 2 */
z = --y; /* z = 1, y = 1 */
/* Arithmetische Operation */
a = a + 12; /* Dies addiert 12 zu a */
a += 12; /* Dies fügt 12 weitere zu a hinzu */
a *= 3,2; /* Dies multipliziert a mit 3,2 */
a -= b; /* Dies subtrahiert b von a */
a /= 10,0; /* Dies dividiert a durch 10,0 */
/* bedingter Ausdruck */
a = (b >= 3,0 ? 2,0 : 10,5 ); /* Dieser Ausdruck */
if (b >= 3.0) /* Und dieser Ausdruck */
a = 2.0; /* sind identisch, beide */
sonst /* wird das gleiche Ergebnis erzielt */
a = 10,5; /* Ergebnis. */
c = (a > b ? a : b); /* c hat das Maximum von a oder b */
c = (a > b ? b : a); /* c hat das Minimum von a oder b */
printf("x=%d, y=%d, z= %d\n", x, y, z);
druckenf("a=%f, b=%f, c= %f", a, b, c);
gebe 0 zurück;
}
und das Ergebnis dieses Programms wird wie folgt auf dem Bildschirm angezeigt:
x=3, y=1, z=1
a=2,000000, b=3,141590, c=2,000000
Noch etwas zu printf() und Scanf()
Betrachten Sie die folgenden beiden printf-Anweisungen
printf(“\t %d\n”, num);
printf(„%5,2f“, Bruch);
In der ersten printf-Anweisung fordert \t die Tabulatorverschiebung auf dem Bildschirm an. Das Argument %d teilt dem Compiler mit, dass der Wert von Num als Dezimalzahl gedruckt werden soll. \n bewirkt, dass die neue Ausgabe in einer neuen Zeile beginnt.
In der zweiten printf-Anweisung teilt %5.2f dem Compiler mit, dass die Ausgabe im Fließkommaformat erfolgen muss, mit insgesamt fünf Stellen und zwei Stellen rechts vom Dezimalpunkt. Weitere Informationen zum Backslash-Zeichen finden Sie in der folgenden Tabelle:
Konstante |
Bedeutung |
'\A' |
Akustischer Alarm (Glocke) |
'\B' |
Rücktaste |
'\F' |
Formularvorschub |
'\N' |
Neue Zeile |
'\R' |
Wagenrücklauf |
'\T' |
Horizontale Registerkarte |
'\v' |
Vertikale Registerkarte |
'\'' |
Einfaches Anführungszeichen |
'\”' |
Doppeltes Anführungszeichen |
'\?' |
Fragezeichen |
'\\' |
Backslash |
'\0' |
Null |
Betrachten wir die folgende scanf-Anweisung
scanf(“%d”, &num);
Die Daten von der Tastatur werden von der Funktion scanf empfangen. Im obigen Format ist das &-Symbol (Et-Zeichen) vor jedem Variablennamen ein Operator, der die Adresse des Variablennamens angibt.
Dadurch wird die Ausführung angehalten und gewartet, bis der Wert der Variablen num eingegeben wird. Wenn der Integer-Wert eingegeben und die Eingabetaste gedrückt wird, fährt der Computer mit der nächsten Anweisung fort. Die Formatcodes scanf und printf sind in der folgenden Tabelle aufgeführt:
Code |
Liest... |
%C |
Einzelnes Zeichen |
%D |
Dezimalzahl |
%Und |
Gleitkommawert |
%F |
Gleitkommawert |
%G |
Gleitkommawert |
%H |
Kurze Ganzzahl |
%ich |
Dezimal-, Hexadezimal- oder Oktalzahl |
%Die |
Oktalzahl |
%S |
Zeichenfolge |
%In |
Vorzeichenlose Dezimalzahl |
%X |
Hexadezimale Ganzzahl |
Kontrollanweisungen
Ein Programm besteht aus einer Reihe von Anweisungen, die normalerweise nacheinander ausgeführt werden. Programme können viel leistungsfähiger sein, wenn wir die Reihenfolge steuern können, in der Anweisungen ausgeführt werden.
Es gibt drei allgemeine Typen von Anweisungen:
- Zuweisung, bei der Werte, normalerweise die Ergebnisse von Berechnungen, in Variablen gespeichert werden.
- Ein-/Ausgabe, Daten werden eingelesen oder ausgedruckt.
- Steuerung: Das Programm entscheidet, was als nächstes zu tun ist.
In diesem Abschnitt wird die Verwendung von Steueranweisungen in C erläutert. Wir zeigen, wie sie zum Schreiben leistungsfähiger Programme verwendet werden können.
- Wiederholung wichtiger Programmteile.
- Auswahl zwischen optionalen Abschnitten eines Programms.
Die if else-Anweisung
Damit lässt sich entscheiden, ob an einem bestimmten Punkt etwas getan werden soll oder zwischen zwei Handlungsmöglichkeiten wählen.
Der folgende Test entscheidet darüber, ob ein Student eine Prüfung mit einer Bestehensnote von 45 bestanden hat
wenn (Ergebnis >= 45)
printf("Pass\n");
anders
printf("Fehlschlag\n");
Es ist möglich, den if-Teil ohne else zu verwenden.
wenn (Temperatur < 0)
drucken("Gefroren\n");
Jede Version besteht aus einem Test in der eingeklammerten Anweisung nach dem „if“. Wenn der Test wahr ist, wird die nächste Anweisung befolgt. Wenn er falsch ist, wird die Anweisung nach dem „else“ befolgt, falls vorhanden. Danach wird der Rest des Programms wie gewohnt fortgesetzt.
Wenn auf das if oder else mehr als eine Anweisung folgen soll, sollten diese in geschweiften Klammern zusammengefasst werden. Eine solche Gruppierung nennt man zusammengesetzte Anweisung oder Block.
wenn (Ergebnis >= 45)
{ printf("Bestanden\n");
printf("Herzlichen Glückwunsch\n");
}
anders
{ printf("Fehlgeschlagen\n");
printf("Nächstes Mal mehr Glück\n");
}
Manchmal möchten wir eine mehrseitige Entscheidung basierend auf mehreren Bedingungen treffen. Die allgemeinste Möglichkeit hierfür ist die Verwendung der else if-Variante der if-Anweisung.
Dies funktioniert durch die Kaskadierung mehrerer Vergleiche. Sobald einer davon ein wahres Ergebnis liefert, wird die folgende Anweisung bzw. der folgende Block ausgeführt und es werden keine weiteren Vergleiche durchgeführt. Im folgenden Beispiel vergeben wir Noten in Abhängigkeit vom Prüfungsergebnis.
wenn (Ergebnis <=100 und Ergebnis >= 75)
printf("Bestanden: Note A\n");
sonst wenn (Ergebnis >= 60)
printf("Bestanden: Note B\n");
sonst wenn (Ergebnis >= 45)
printf("Bestanden: Note C\n");
anders
printf("Fehlgeschlagen\n");
In diesem Beispiel testen alle Vergleiche eine einzelne Variable namens „result“. In anderen Fällen kann jeder Test eine andere Variable oder eine Kombination von Tests beinhalten. Das gleiche Muster kann mit mehr oder weniger else if's verwendet werden, und das letzte else kann weggelassen werden.
Es liegt am Programmierer, für jedes Programmierproblem die richtige Struktur zu entwickeln. Um die Verwendung von if else besser zu verstehen, sehen wir uns das Beispiel an
#include <stdio.h>
int main()
{
int-num;
für (Zahl = 0; Zahl < 10; Zahl = Zahl + 1)
{
wenn (Zahl == 2)
printf("num ist jetzt gleich %d\n", num);
wenn (Zahl < 5)
printf("num ist jetzt %d, also kleiner als 5\n", num);
anders
printf("num ist jetzt %d, was größer als 4\n ist", num);
} /* Ende der for-Schleife */
gebe 0 zurück;
}
Ergebnis des Programms
num ist jetzt 0, was kleiner als 5 ist
num ist jetzt 1, was kleiner als 5 ist
num ist jetzt gleich 2
num ist jetzt 2, was kleiner als 5 ist
num ist jetzt 3, was kleiner als 5 ist
num ist jetzt 4, was kleiner als 5 ist
num ist jetzt 5, was größer als 4 ist
num ist jetzt 6, was größer als 4 ist
num ist jetzt 7, was größer als 4 ist
num ist jetzt 8, was größer als 4 ist
num ist jetzt 9, was größer als 4 ist
Die switch-Anweisung
Dies ist eine weitere Form der Mehrwegentscheidung. Sie ist gut strukturiert, kann aber nur in bestimmten Fällen verwendet werden, wenn:
- Es wird nur eine Variable getestet, alle Verzweigungen müssen vom Wert dieser Variable abhängen. Die Variable muss ein ganzzahliger Typ sein (int, long, short oder char).
- Jeder mögliche Wert der Variablen kann einen einzelnen Zweig steuern. Ein letzter, alles abfangender Standardzweig kann optional verwendet werden, um alle nicht angegebenen Fälle abzufangen.
Das unten aufgeführte Beispiel wird die Dinge verdeutlichen. Dies ist eine Funktion, die eine Ganzzahl in eine vage Beschreibung umwandelt. Sie ist nützlich, wenn wir nur eine Menge messen möchten, die relativ klein ist.
Schätzung (Zahl)
int-Zahl;
/* Schätzen Sie eine Zahl als keine, eins, zwei, mehrere, viele */
{ Schalter(Nummer) {
Fall 0:
printf("Keine\n");
brechen;
Fall 1:
printf("Eins\n");
brechen;
Fall 2:
printf("Zwei\n");
brechen;
Fall 3:
Fall 4:
Fall 5:
printf("Mehrere\n");
brechen;
Standard :
printf("Viele\n");
brechen;
}
}
Jeder interessante Fall wird mit einer entsprechenden Aktion aufgeführt. Die break-Anweisung verhindert, dass weitere Anweisungen ausgeführt werden, indem der Schalter verlassen wird. Da Fall 3 und Fall 4 kein nachfolgendes break haben, werden sie fortgesetzt und ermöglichen die gleiche Aktion für mehrere Zahlenwerte.
Sowohl if- als auch switch-Konstrukte ermöglichen dem Programmierer, eine Auswahl aus einer Reihe möglicher Aktionen zu treffen. Sehen wir uns ein Beispiel an:
#include <stdio.h>
int main()
{
int-num;
für (num = 3; num < 13; num = num + 1)
{
Schalter (Nummer)
{
Fall 3:
printf("Der Wert ist drei\n");
brechen;
Fall 4:
printf("Der Wert ist vier\n");
brechen;
Fall 5:
Fall 6:
Fall 7:
Fall 8:
printf("Der Wert liegt zwischen 5 und 8\n");
brechen;
Fall 11:
printf("Der Wert ist elf\n");
brechen;
Standard :
printf("Es ist einer der undefinierten Werte\n");
brechen;
} /* Ende des Schalters */
} /* Ende der for-Schleife */
gebe 0 zurück;
}
Die Ausgabe des Programms lautet
Der Wert ist drei
Der Wert ist vier
Der Wert liegt zwischen 5 und 8
Der Wert liegt zwischen 5 und 8
Der Wert liegt zwischen 5 und 8
Der Wert liegt zwischen 5 und 8
Es ist einer der undefinierten Werte
Es ist einer der undefinierten Werte
Der Wert ist elf
Es ist einer der undefinierten Werte
Die break-Anweisung
Break ist uns bereits bei der Diskussion der Switch-Anweisung begegnet. Es wird verwendet, um eine Schleife oder einen Switch zu verlassen, wobei die Steuerung an die erste Anweisung nach der Schleife oder dem Switch übergeben wird.
Bei Schleifen kann break verwendet werden, um ein vorzeitiges Verlassen der Schleife zu erzwingen oder eine Schleife mit einem Test zum Verlassen in der Mitte des Schleifenkörpers zu implementieren. Ein break innerhalb einer Schleife sollte immer innerhalb einer if-Anweisung geschützt werden, die den Test zur Steuerung der Beendigungsbedingung bereitstellt.
Die continue-Anweisung
Dies ähnelt break, wird aber seltener verwendet. Es funktioniert nur innerhalb von Schleifen, wo es einen sofortigen Sprung zur Schleifenkontrollanweisung erzwingt.
- Springen Sie in einer While-Schleife zur Testanweisung.
- Springen Sie in einer do while-Schleife zur Testanweisung.
- Springen Sie in einer For-Schleife zum Test und führen Sie die Iteration durch.
Wie ein „break“ sollte „continue“ durch eine „if“-Anweisung geschützt werden. Sie werden diese wahrscheinlich nicht sehr oft verwenden. Um die Verwendung von „break“ und „continue“ besser zu verstehen, sehen wir uns das folgende Programm an:
#include <stdio.h>
int main()
{
int-Wert;
für (Wert = 5; Wert < 15; Wert = Wert + 1)
{
wenn (Wert == 8)
brechen;
printf("In der Break-Schleife ist der Wert jetzt %d\n", value);
}
für (Wert = 5; Wert < 15; Wert = Wert + 1)
{
wenn (Wert == 8)
weitermachen;
printf("In der Weiterschleife ist der Wert jetzt %d\n", value);
}
gebe 0 zurück;
}
Die Ausgabe des Programms sieht wie folgt aus:
In der Break-Schleife ist der Wert jetzt 5
In der Break-Schleife ist der Wert jetzt 6
In der Break-Schleife ist der Wert jetzt 7
In der Weiterschleife ist der Wert jetzt 5
In der Weiterschleife ist der Wert jetzt 6
In der Weiterschleife ist der Wert jetzt 7
In der Weiterschleife ist der Wert jetzt 9
In der Weiterschleife ist der Wert jetzt 10
In der Weiterschleife ist der Wert jetzt 11
In der Weiterschleife ist der Wert jetzt 12
In der Weiterschleife ist der Wert jetzt 13
In der Weiterschleife ist der Wert jetzt 14
Schleifen
Der andere Haupttyp von Kontrollanweisungen ist die Schleife. Schleifen ermöglichen die Wiederholung einer Anweisung oder eines Anweisungsblocks. Computer sind sehr gut darin, einfache Aufgaben viele Male zu wiederholen. Die Schleife ist die Art und Weise, wie C dies erreicht.
C bietet Ihnen die Wahl zwischen drei Schleifentypen: while, do-while und for.
- Die While-Schleife wiederholt eine Aktion so lange, bis ein zugehöriger Test „false“ zurückgibt. Dies ist nützlich, wenn der Programmierer nicht im Voraus weiß, wie oft die Schleife durchlaufen wird.
- Die do while-Schleifen sind ähnlich, aber der Test erfolgt nach der Ausführung des Schleifenkörpers. Dadurch wird sichergestellt, dass der Schleifenkörper mindestens einmal ausgeführt wird.
- Die For-Schleife wird häufig verwendet, normalerweise wenn die Schleife eine festgelegte Anzahl von Malen durchlaufen wird. Sie ist sehr flexibel und unerfahrene Programmierer sollten darauf achten, ihre Leistungsfähigkeit nicht zu missbrauchen.
Die while-Schleife
Die while-Schleife wiederholt eine Anweisung, bis der Test oben falsch ist. Als Beispiel sehen Sie hier eine Funktion, die die Länge einer Zeichenfolge zurückgibt. Denken Sie daran, dass die Zeichenfolge als Array von Zeichen dargestellt wird, das durch ein Nullzeichen „\0“ abgeschlossen wird.
int string_length(Zeichenfolge[])
} { int i = 0;
während (Zeichenfolge[i] != '\0')
ich++;
Rückkehr(i);
}
Der String wird der Funktion als Argument übergeben. Die Größe des Arrays ist nicht angegeben, die Funktion funktioniert mit Strings beliebiger Größe.
Die while-Schleife dient dazu, die Zeichen in der Zeichenfolge einzeln zu durchsuchen, bis das Nullzeichen gefunden wird. Anschließend wird die Schleife verlassen und der Index des Nullzeichens zurückgegeben.
Solange das Zeichen nicht null ist, wird der Index erhöht und der Test wiederholt. Wir werden uns später ausführlich mit Arrays befassen. Sehen wir uns ein Beispiel für eine While-Schleife an:
#include <stdio.h>
int main()
{
int-Anzahl;
Anzahl = 0;
während (Anzahl < 6)
{
printf("Der Wert von count ist %d\n", count);
Anzahl = Anzahl + 1;
}
gebe 0 zurück;
}
und das Ergebnis wird wie folgt angezeigt:
Der Wert von count ist 0
Der Wert von count ist 1
Der Wert von count ist 2
Der Wert von count ist 3
Der Wert von count ist 4
Der Wert von count ist 5
Die do while-Schleife
Dies ist der while-Schleife sehr ähnlich, außer dass der Test am Ende des Schleifenkörpers erfolgt. Dies garantiert, dass die Schleife mindestens einmal ausgeführt wird, bevor sie fortgesetzt wird.
Ein solches Setup wird häufig verwendet, wenn Daten gelesen werden müssen. Der Test überprüft dann die Daten und führt eine Schleife zurück, um sie erneut zu lesen, wenn sie nicht akzeptabel waren.
Tun
{
printf("Geben Sie 1 für ja und 0 für nein ein:");
scanf("%d", &Eingabewert);
} während (Eingabewert != 1 und Eingabewert != 0)
Zum besseren Verständnis der do while-Schleife sehen wir uns das folgende Beispiel an:
#include <stdio.h>
int main()
{
int ich;
ich = 0;
Tun
{
printf("Der Wert von i ist jetzt %d\n", i);
ich = ich + 1;
} während (i < 5);
gebe 0 zurück;
}
Das Ergebnis des Programms wird wie folgt angezeigt:
Der Wert von i ist jetzt 0
Der Wert von i ist jetzt 1
Der Wert von i ist jetzt 2
Der Wert von i ist jetzt 3
Der Wert von i ist jetzt 4
Die for-Schleife
Die for-Schleife funktioniert gut, wenn die Anzahl der Iterationen der Schleife bekannt ist, bevor die Schleife aufgerufen wird. Der Kopf der Schleife besteht aus drei durch Semikolon getrennten Teilen.
- Der erste wird ausgeführt, bevor die Schleife betreten wird. Dies ist normalerweise die Initialisierung der Schleifenvariable.
- Der Zweite ist ein Test; die Schleife wird verlassen, wenn dieser „false“ zurückgibt.
- Die dritte Anweisung ist eine Anweisung, die jedes Mal ausgeführt wird, wenn der Schleifenkörper abgeschlossen ist. Dies ist normalerweise eine Erhöhung des Schleifenzählers.
Das Beispiel ist eine Funktion, die den Durchschnitt der in einem Array gespeicherten Zahlen berechnet. Die Funktion verwendet das Array und die Anzahl der Elemente als Argumente.
Float-Durchschnitt (Float-Array [], Int-Anzahl)
{
Gleitkommazahl gesamt = 0,0;
int ich;
für(i = 0; i < Anzahl; i++)
Gesamt + = Array [i];
return(Gesamt / Anzahl);
}
Die For-Schleife stellt sicher, dass die richtige Anzahl von Array-Elementen addiert wird, bevor der Durchschnitt berechnet wird.
Die drei Anweisungen am Anfang einer For-Schleife führen normalerweise jeweils nur eine Funktion aus, aber jede von ihnen kann leer gelassen werden. Eine leere erste oder letzte Anweisung bedeutet, dass keine Initialisierung oder kein laufender Inkrement erfolgt. Eine leere Vergleichsanweisung wird immer als wahr behandelt. Dies führt dazu, dass die Schleife unbegrenzt weiterläuft, sofern sie nicht auf andere Weise unterbrochen wird. Dies kann eine Return- oder Break-Anweisung sein.
Es ist auch möglich, mehrere Anweisungen in die erste oder dritte Position zu quetschen und sie durch Kommas zu trennen. Dies ermöglicht eine Schleife mit mehr als einer Steuervariable. Das folgende Beispiel veranschaulicht die Definition einer solchen Schleife, wobei die Variablen hi und lo bei 100 bzw. 0 beginnen und konvergieren.
Die for-Schleife bietet eine Vielzahl von Abkürzungen, die darin verwendet werden können. Achten Sie auf den folgenden Ausdruck, in diesem Ausdruck enthält die einzelne Schleife zwei for-Schleifen. Hier ist hi-- dasselbe wie hi = hi - 1 und lo++ ist dasselbe wie lo = lo + 1,
für (hi = 100, lo = 0; hi >= lo; hi--, lo++)
Die For-Schleife ist äußerst flexibel und ermöglicht die einfache und schnelle Festlegung vieler Arten von Programmverhalten. Sehen wir uns ein Beispiel für eine For-Schleife an
#include <stdio.h>
int main()
{
int-Index;
für (Index = 0; Index < 6; Index = Index + 1)
printf("Der Wert des Index ist %d\n", index);
gebe 0 zurück;
}
Das Ergebnis des Programms wird wie folgt angezeigt:
Der Indexwert ist 0
Der Indexwert ist 1
Der Indexwert ist 2
Der Indexwert beträgt 3
Der Indexwert beträgt 4
Der Indexwert beträgt 5
Die goto-Anweisung
C hat eine goto-Anweisung, die unstrukturierte Sprünge erlaubt. Um eine goto-Anweisung zu verwenden, verwenden Sie einfach das reservierte Wort goto, gefolgt vom symbolischen Namen, zu dem Sie springen möchten. Der Name wird dann an eine beliebige Stelle im Programm eingefügt, gefolgt von einem Doppelpunkt. Sie können innerhalb einer Funktion fast überall hin springen, aber Sie dürfen nicht in eine Schleife springen, obwohl Sie aus einer Schleife herausspringen dürfen.
This particular program is really a mess but it is a good example of why software writers are trying to eliminate the use of the goto statement as much as possible. The only place in this program where it is reasonable to use the goto is, where the program jumps out of the three nested loops in one jump. In this case it would be rather messy to set up a variable and jump successively out of each of the three nested loops but one goto statement gets you out of all three in a very concise manner.
Some persons say the goto statement should never be used under any circumstances, but this is narrow minded thinking. If there is a place where a goto will clearly do a neater control flow than some other construct, feel free to use it, however, as it is in the rest of the program on your monitor. Let us see the example:
#include <stdio.h>
int main()
{
int dog, cat, pig;
goto real_start;
some_where:
printf("This is another line of the mess.\n");
goto stop_it;
/* the following section is the only section with a useable goto */
real_start:
for(dog = 1 ; dog < 6 ; dog = dog + 1)
{
for(cat = 1 ; cat < 6 ; cat = cat + 1)
{
for(pig = 1 ; pig < 4 ; pig = pig + 1)
{
printf("Dog = %d Cat = %d Pig = %d\n", dog, cat, pig);
if ((dog + cat + pig) > 8 ) goto enough;
}
}
}
enough: printf("Those are enough animals for now.\n");
/* this is the end of the section with a useable goto statement */
printf("\nThis is the first line of the code.\n");
goto there;
where:
printf("This is the third line of the code.\n");
goto some_where;
there:
printf("This is the second line of the code.\n");
goto where;
stop_it:
printf("This is the last line of this mess.\n");
return 0;
}
Let us see the results displayed
Dog = 1 Cat = 1 Pig = 1
Dog = 1 Cat = 1 Pig = 2
Dog = 1 Cat = 1 Pig = 3
Dog = 1 Cat = 2 Pig = 1
Dog = 1 Cat = 2 Pig = 2
Dog = 1 Cat = 2 Pig = 3
Dog = 1 Cat = 3 Pig = 1
Dog = 1 Cat = 3 Pig = 2
Dog = 1 Cat = 3 Pig = 3
Dog = 1 Cat = 4 Pig = 1
Dog = 1 Cat = 4 Pig = 2
Dog = 1 Cat = 4 Pig = 3
Dog = 1 Cat = 5 Pig = 1
Dog = 1 Cat = 5 Pig = 2
Dog = 1 Cat = 5 Pig = 3
Those are enough animals for now.
This is the first line of the code.
This is the second line of the code.
This is the third line of the code.
This is another line of the mess.
This is the last line of this mess.
Pointers
Sometimes we want to know where a variable resides in memory. A pointer contains the address of a variable that has a specific value. When declaring a pointer, an asterisk is placed immediately before the pointer name.
The address of the memory location where the variable is stored can be found by placing an ampersand in front of the variable name.
int num; /* Normal integer variable */
int *numPtr; /* Pointer to an integer variable */
The following example prints the variable value and the address in memory of that variable.
printf("The value %d is stored at address %X\n", num, &num);
To assign the address of the variable num to the pointer numPtr, you assign the address of the variable, num, as in the example given next:
numPtr = #
To find out what is stored at the address pointed to by numPtr, the variable needs to be dereferenced. Dereferencing is achieved with the asterisk that the pointer was declared with.
printf("The value %d is stored at address %X\n", *numPtr, numPtr);
All variables in a program reside in memory. The statements given below request that the compiler reserve 4 bytes of memory on a 32-bit computer for the floating-point variable x, then put the value 6.5 in it.
float x;
x = 6.5;
As the address location in memory of any variable is obtained by placing the operator & before its name therefore &x is the address of x. C allows us to go one stage further and define a variable, called a pointer that contains the address of other variables. Rather we can say that pointer points to other variable. For example:
float x;
float* px;
x = 6.5;
px = &x;
defines px to be a pointer to objects of type float, and sets it equal to the address of x. Thus, *px refers to the value of x:

Let us examine the following statements:
int var_x;
int* ptrX;
var_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("value of x : %d", var_x);
The first line causes the compiler to reserve a space in memory for an integer. The second line tells the compiler to reserve space to store a pointer.
A pointer is a storage location for an address. The third line should remind you the scanf statements. The address "&" operator tells compiler to go to the place it stored var_x, and then give the address of the storage location to ptrX.
The asterisk * in front of a variable tells the compiler to dereference the pointer, and go to memory. Then you can make assignments to variable stored at that location. You can reference a variable and access its data through a pointer. Let us see an example of pointers:
/* illustration of pointer use */
#include <stdio.h>
int main()
{
int index, *pt1, *pt2;
index = 39; /* any numerical value */
pt1 = &index; /* the address of index */
pt2 = pt1;
printf("Der Wert ist %d %d %d\n", index, *pt1, *pt2);
*pt1 = 13; /* dies ändert den Wert des Index */
printf("Der Wert ist %d %d %d\n", index, *pt1, *pt2);
gebe 0 zurück;
}
Die Ausgabe des Programms wird wie folgt angezeigt:
Der Wert ist 39 39 39
Der Wert ist 13 13 13
Sehen wir uns ein weiteres Beispiel an, um die Verwendung von Zeigern besser zu verstehen:
#include <stdio.h>
#include <string.h>
int main()
{
char strg[40], *da, eins, zwei;
int *pt, Liste[100], Index;
strcpy(strg, "Dies ist eine Zeichenfolge.");
/* Die Funktion strcpy() dient zum Kopieren eines Strings in einen anderen. Über die Funktion strcpy() lesen wir später im Abschnitt Strings */
eins = strg[0]; /* eins und zwei sind identisch */
two = *strg;
printf("Die erste Ausgabe ist %c %c\n", eins, zwei);
eins = strg[8]; /* eins und zwei sind identisch */
two = *(strg+8);
printf("Die zweite Ausgabe ist %c %c\n", eins, zwei);
there = strg+10; /* strg+10 is identical to &strg[10] */
printf("Die dritte Ausgabe ist %c\n", strg[10]);
printf("Die vierte Ausgabe ist %c\n", *da);
für (Index = 0; Index < 100; Index++)
Liste[Index] = Index + 100;
pt = Liste + 27;
printf("Die fünfte Ausgabe ist %d\n", list[27]);
printf("Die sechste Ausgabe ist %d\n", *pt);
gebe 0 zurück;
}
Die Ausgabe des Programms sieht folgendermaßen aus:
Der erste Ausgang ist TT
Der zweite Ausgang ist aa
Der dritte Ausgang ist c
Der vierte Ausgang ist c
Der fünfte Ausgang ist 127
Der sechste Ausgang ist 127
Arrays
Ein Array ist eine Sammlung von Variablen desselben Typs. Die einzelnen Array-Elemente werden durch einen ganzzahligen Index identifiziert. In C beginnt der Index bei Null und wird immer in eckige Klammern geschrieben.
Wir haben bereits eindimensionale Arrays kennengelernt, die wie folgt deklariert sind
int Ergebnisse[20];
Arrays können mehr Dimensionen haben. In diesem Fall können sie wie folgt deklariert werden:
int Ergebnisse_2d[20][5];
int Ergebnisse_3d[20][5][3];
Jeder Index hat seinen eigenen Satz eckiger Klammern. Ein Array wird in der Hauptfunktion deklariert und enthält normalerweise Angaben zu den Dimensionen. Anstelle eines Arrays kann auch ein anderer Typ verwendet werden, der als Zeiger bezeichnet wird. Dies bedeutet, dass die Dimensionen nicht sofort festgelegt sind, sondern der Speicherplatz nach Bedarf zugewiesen werden kann. Dies ist eine fortgeschrittene Technik, die nur in bestimmten Spezialprogrammen erforderlich ist.
Als Beispiel hier eine einfache Funktion zum Addieren aller Ganzzahlen in einem eindimensionalen Array.
int add_array(int array[], int größe)
{
int ich;
int total = 0;
for(i = 0; i < size; i++)
total += array[i];
return(total);
}
The program given next will make a string, access some data in it, print it out. Access it again using pointers, and then print the string out. It should print “Hi!” and “012345678” on different lines. Let us see the coding of the program:
#include <stdio.h>
#define STR_LENGTH 10
void main()
{
char Str[STR_LENGTH];
char* pStr;
int i;
Str[0] = 'H';
Str[1] = 'i';
Str[2] = '!';
Str[3] = '\0'; // special end string character NULL
printf("The string in Str is : %s\n", Str);
pStr = &Str[0];
for (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("The string in Str is : %s\n", Str);
}
[] (square braces) are used to declare the array. The line of the program char Str[STR_LENGTH]; declares an array of ten characters. These are ten individual characters, which are all put together in memory into the same place. They can all be accessed through our variable name Str along with a [n] where n is the element number.
It should always be kept in mind when talking about array that when C declares an array of ten, the elements you can access are numbered 0 to 9. Accessing the first element corresponds to accessing the 0th element. So in case of Arrays always count from 0 to size of array - 1.
Next notice that we put the letters "Hi!" into the array, but then we put in a '\0' you are probably wondering what this is. "\0" stands for NULL and represents the end of string. All character strings need to end with this special character '\0'. If they do not, and then someone calls printf on the string, then printf would start at the memory location of your string, and continue printing tell it encounters '\0' and thus you will end up with a bunch of garbage at the end of your string. So make sure to terminate your strings properly.
Character Arrays
A string constant , such as
"I am a string"
is an array of characters. It is represented internally in C by the ASCII characters in the string, i.e., “I”, blank, “a”, “m”,…or the above string, and terminated by the special null character “\0” so programs can find the end of the string.
String constants are often used in making the output of code intelligible using printf:
printf("Hello, world\n");
printf("The value of a is: %f\n", a);
String constants can be associated with variables. C provides the character type variable, which can contain one character (1 byte) at a time. A character string is stored in an array of character type, one ASCII character per location.
Vergessen Sie nie, dass wir einen zusätzlichen Speicherplatz im Array benötigen, da Zeichenfolgen üblicherweise durch das Nullzeichen „\0“ beendet werden.
C bietet keinen Operator, der ganze Strings auf einmal manipuliert. Strings werden entweder über Zeiger oder über spezielle Routinen manipuliert, die in der Standard-String-Bibliothek string.h verfügbar sind.
Die Verwendung von Zeichenzeigern ist relativ einfach, da der Name eines Arrays nur ein Zeiger auf sein erstes Element ist. Betrachten Sie das folgende Programm:
#include<stdio.h>
void main()
{
Zeichen text_1[100], text_2[100], text_3[100];
verkohlen *ta, *tb;
int ich;
/* Nachricht als Array festlegen */
/* von Zeichen; initialisiere es */
/* in die konstante Zeichenfolge "..." */
/* überlassen Sie die Entscheidung dem Compiler */
/* seine Größe durch Verwendung von [] */
char message[] = "Hallo, ich bin ein String. Was sind
Du?";
printf("Ursprüngliche Nachricht: %s\n", Nachricht);
/* kopiere die Nachricht nach text_1 */
ich = 0;
während ( (text_1[i] = nachricht[i]) != '\0' )
ich++;
printf("Text_1: %s\n", text_1);
/* explizite Zeigerarithmetik verwenden */
Ihre=Nachricht;
tb=text_2;
während ( ( *tb++ = *ta++ ) != '\0' )
;
printf("Text_2: %s\n", text_2);
}
Die Ausgabe des Programms sieht wie folgt aus:
Originalnachricht: Hallo, ich bin eine Zeichenfolge. Was bist du?
Text_1: Hallo, ich bin eine Zeichenfolge. Was bist du?
Text_2: Hallo, ich bin eine Zeichenfolge. Was bist du?
Die Standardbibliothek „String“ enthält viele nützliche Funktionen zur Manipulation von Strings, die wir später im Abschnitt Strings kennenlernen werden.
Zugriff auf die Elemente
Um auf ein einzelnes Element im Array zuzugreifen, folgt die Indexnummer dem Variablennamen in eckigen Klammern. Die Variable kann dann wie jede andere Variable in C behandelt werden. Das folgende Beispiel weist dem ersten Element im Array einen Wert zu.
x[0] = 16;
Das folgende Beispiel druckt den Wert des dritten Elements in einem Array.
printf("%d\n", x[2]);
Das folgende Beispiel verwendet die Funktion scanf, um einen Wert von der Tastatur in das letzte Element eines Arrays mit zehn Elementen einzulesen.
scanf("%d", &x[9]);
Initialisieren von Array-Elementen
Arrays können wie jede andere Variable per Zuweisung initialisiert werden. Da ein Array mehr als einen Wert enthält, werden die einzelnen Werte in geschweifte Klammern gesetzt und durch Kommas getrennt. Das folgende Beispiel initialisiert ein zehndimensionales Array mit den ersten zehn Werten der drei Einmaleinsreihen.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Dadurch wird das individuelle Zuweisen der Werte, wie im folgenden Beispiel, erspart.
int x[10];
x[0] = 3;
x[1] = 6;
x[2] = 9;
x[3] = 12;
x[4] = 15;
x[5] = 18;
x[6] = 21;
x[7] = 24;
x[8] = 27;
x[9] = 30;
Durchlaufen eines Arrays
Da das Array sequenziell indiziert ist, können wir die for-Schleife verwenden, um alle Werte eines Arrays anzuzeigen. Das folgende Beispiel zeigt alle Werte eines Arrays an:
#include <stdio.h>
int main()
{
int x[10];
int-Zähler;
/* Zufallszahlengenerator randomisieren */
srand((unsigned)Zeit(NULL));
/* Der Variablen zufällige Werte zuweisen */
für (Zähler=0; Zähler<10; Zähler++)
x[Zähler] = rand();
/* Den Inhalt des Arrays anzeigen */
für (Zähler=0; Zähler<10; Zähler++)
printf("Element %d hat den Wert %d\n", Zähler, x[Zähler]);
gebe 0 zurück;
}
Obwohl die Ausgabe jedes Mal unterschiedliche Werte ausgibt, wird das Ergebnis ungefähr so angezeigt:
Element 0 hat den Wert 17132
Element 1 hat den Wert 24904
Element 2 hat den Wert 13466
Element 3 hat den Wert 3147
Element 4 hat den Wert 22006
Element 5 hat den Wert 10397
Element 6 hat den Wert 28114
Element 7 hat den Wert 19817
Element 8 hat den Wert 27430
Element 9 hat den Wert 22136
Mehrdimensionale Arrays
Ein Array kann mehr als eine Dimension haben. Mehrdimensionale Arrays bieten mehr Flexibilität. Tabellenkalkulationen basieren beispielsweise auf einem zweidimensionalen Array: einem Array für die Zeilen und einem Array für die Spalten.
Das folgende Beispiel verwendet ein zweidimensionales Array mit zwei Zeilen, die jeweils fünf Spalten enthalten:
#include <stdio.h>
int main()
{
/* Deklariere ein mehrdimensionales 2 x 5 Array */
int x[2][5] = { {1, 2, 3, 4, 5},
{2, 4, 6, 8, 10} };
int Zeile, Spalte;
/* Zeilen anzeigen */
für (Zeile=0; Zeile<2; Zeile++)
{
/* Die Spalten anzeigen */
für (Spalte=0; Spalte<5; Spalte++)
printf("%d\t", x[Zeile][Spalte]);
setchar('\n');
}
gebe 0 zurück;
}
Die Ausgabe dieses Programms wird wie folgt angezeigt:
1 2 3 4 5
2 4 6 8 10
Saiten
Ein String ist eine Gruppe von Zeichen, normalerweise Buchstaben des Alphabets. Damit können Sie Ihre Druckanzeige so formatieren, dass sie gut aussieht, aussagekräftige Namen und Titel hat und für Sie und die Benutzer der Ausgabe Ihres Programms ästhetisch ansprechend ist.
Tatsächlich haben Sie in den Beispielen der vorherigen Themen bereits Zeichenfolgen verwendet. Dies ist jedoch nicht die vollständige Einführung in Zeichenfolgen. Es gibt viele mögliche Fälle in der Programmierung, in denen die Verwendung formatierter Zeichenfolgen dem Programmierer hilft, zu viele Komplikationen im Programm und natürlich zu viele Fehler zu vermeiden.
A complete definition of a string is a series of character type data terminated by a null character (‘\0’).
When C is going to use a string of data in some way, either to compare it with another string, output it, copy it to another string, or whatever, the functions are set up to do what they are called to do until a null is detected.
There is no basic data type for a string in C Instead; strings in C are implemented as an array of characters. For example, to store a name you could declare a character array big enough to store the name, and then use the appropriate library functions to manipulate the name.
The following example displays the string on the screen, entered by user:
#include <stdio.h>
int main()
{
char name[80]; /* Create a character array
called name */
printf("Enter your name: ");
gets(name);
printf("The name you entered was %s\n", name);
return 0;
}
The execution of the program will be:
Enter your name: Tarun Tyagi
The name you entered was Tarun Tyagi
Some Common String Functions
The standard string.h library contains many useful functions to manipulate strings. Some of the most useful functions have been exampled here.
The strlen Function
The strlen function is used to determine the length of a string. Let us learn the use of strlen with example:
#include <stdio.h>
#include <string.h>
int main()
{
char name[80];
int length;
printf("Enter your name: ");
gets(name);
length = strlen(name);
printf("Your name has %d characters\n", length);
return 0;
}
And the execution of the program will be as follows:
Enter your name: Tarun Subhash Tyagi
Your name has 19 characters
Enter your name: Preeti Tarun
Your name has 12 characters
The strcpy Function
The strcpy function is used to copy one string to another. Let us learn the use of this function with example:
#include <stdio.h>
#include <string.h>
int main()
{
char first[80];
char second[80];
printf("Enter first string: ");
gets(first);
printf("Enter second string: ");
gets(second);
printf("first: %s, and second: %s Before strcpy()\n "
, first, second);
strcpy(second, first);
printf("first: %s, and second: %s After strcpy()\n",
first, second);
return 0;
}
and the output of the program will be as:
Enter first string: Tarun
Enter second string: Tyagi
first: Tarun, and second: Tyagi Before strcpy()
erstens: Tarun und zweitens: Tarun Nach strcpy()
Die strcmp-Funktion
Die Funktion strcmp wird verwendet, um zwei Zeichenfolgen miteinander zu vergleichen. Der Variablenname eines Arrays zeigt auf die Basisadresse dieses Arrays. Wenn wir also versuchen, zwei Zeichenfolgen mithilfe des Folgenden zu vergleichen, würden wir zwei Adressen vergleichen, die offensichtlich nie gleich wären, da es nicht möglich ist, zwei Werte am selben Ort zu speichern.
if (first == second) /* Das Vergleichen von Zeichenfolgen ist nie möglich */
Im folgenden Beispiel wird die Funktion strcmp zum Vergleichen zweier Zeichenfolgen verwendet:
#include <string.h>
int main()
{
Zeichen erster[80], zweiter[80];
int t;
für (t=1;t<=2;t++)
{
printf("\nGeben Sie eine Zeichenfolge ein: ");
bekommt (zuerst);
printf("Geben Sie eine andere Zeichenfolge ein: ");
bekommt (Sekunde);
wenn (strcmp(erster, zweiter) == 0)
puts("Die beiden Zeichenfolgen sind gleich");
anders
puts("Die beiden Zeichenfolgen sind nicht gleich");
}
gebe 0 zurück;
}
Und die Ausführung des Programms erfolgt wie folgt:
Geben Sie eine Zeichenfolge ein: Tarun
Geben Sie eine andere Zeichenfolge ein: tarun
Die beiden Zeichenfolgen sind nicht gleich
Geben Sie eine Zeichenfolge ein: Tarun
Geben Sie eine andere Zeichenfolge ein: Tarun
Die beiden Zeichenfolgen sind gleich
Die strcat-Funktion
Die Funktion strcat wird verwendet, um einen String mit einem anderen zu verbinden. Sehen wir uns an, wie das geht. Anhand eines Beispiels:
#include <string.h>
int main()
{
Zeichen erster[80], zweiter[80];
printf("Geben Sie eine Zeichenfolge ein: ");
bekommt (zuerst);
printf("Geben Sie eine andere Zeichenfolge ein: ");
bekommt (Sekunde);
strcat(erster, zweiter);
printf("Die beiden zusammengefügten Zeichenfolgen: %s\n",
Erste);
gebe 0 zurück;
}
Und die Ausführung des Programms erfolgt wie folgt:
Geben Sie eine Zeichenfolge ein: Daten
Geben Sie eine andere Zeichenfolge ein: Wiederherstellung
Die beiden Zeichenfolgen werden miteinander verbunden: DatenWiederherstellung
Die strtok-Funktion
Die Funktion strtok wird verwendet, um das nächste Token in einer Zeichenfolge zu finden. Das Token wird durch eine Liste möglicher Trennzeichen angegeben.
Das folgende Beispiel liest eine Textzeile aus einer Datei und ermittelt anhand der Trennzeichen Leerzeichen, Tabulator und Zeilenumbruch ein Wort. Jedes Wort wird dann in einer eigenen Zeile angezeigt:
#include <stdio.h>
#include <string.h>
int main()
{
DATEI *in;
Zeichenzeile[80];
char *Trennzeichen = " \t\n";
Zeichen *Token;
wenn ((in = fopen("C:\\text.txt", "r")) == NULL)
{
puts("Die Eingabedatei konnte nicht geöffnet werden");
gebe 0 zurück;
}
/* Jede Zeile einzeln lesen */
während(!feof(in))
{
/* Eine Zeile holen */
fgets(Zeile, 80, in);
wenn (!feof(in))
{
/* Die Zeile in Wörter aufteilen */
Token = strtok(Zeile, Trennzeichen);
während (Token != NULL)
{
setzt (Token);
/* Hol das nächste Wort */
Token = strtok(NULL, Trennzeichen);
}
}
}
fclose(in);
gebe 0 zurück;
}
Das obige Programm, in = fopen("C:\\text.txt", "r"), öffnet eine vorhandene Datei C:\\text.txt. Wenn die Datei im angegebenen Pfad nicht vorhanden ist oder aus irgendeinem Grund nicht geöffnet werden konnte, wird eine Fehlermeldung auf dem Bildschirm angezeigt.
Betrachten Sie das folgende Beispiel, das einige dieser Funktionen verwendet:
#include <stdio.h>
#include <string.h>
void main()
{
Zeichenzeile[100], *Untertext;
/* Zeichenfolge initialisieren */
strcpy(line,"hallo, ich bin ein String;");
printf("Zeile: %s\n", Zeile);
/* am Ende der Zeichenfolge hinzufügen */
strcat(line,"was bist du?");
printf("Zeile: %s\n", Zeile);
/* Länge der Zeichenfolge ermitteln */
/* strlen bringt zurück */
/* Länge als Typ size_t */
printf("Länge der Zeile: %d\n", (int)strlen(line));
/* Vorkommen von Teilstrings finden */
wenn ( (Untertext = strchr (Zeile, 'W' ) )!= NULL )
printf("Zeichenfolge beginnt mit \"W\" ->%s\n",
Untertext);
wenn ( ( Untertext = strchr ( Zeile, 'w' ) )!= NULL )
printf("Zeichenfolge beginnt mit \"w\" ->%s\n",
Untertext);
wenn ( ( sub_text = strchr ( sub_text, 'u' ) )!= NULL )
printf("Zeichenfolge beginnt mit \"w\" ->%s\n",
Untertext);
}
Die Ausgabe des Programms wird wie folgt angezeigt:
Zeile: Hallo, ich bin eine Zeichenfolge;
Zeile: Hallo, ich bin eine Zeichenfolge. Was bist du?
Länge der Linie: 35
Zeichenfolge, die mit „w“ beginnt -> was bist du?
Zeichenfolge beginnend mit „w“ ->u?
Funktionen
Die beste Methode zum Entwickeln und Verwalten eines großen Programms besteht darin, es aus kleineren Teilen zusammenzusetzen, die jeweils leichter zu verwalten sind (eine Technik, die manchmal als „Teile und herrsche“ bezeichnet wird). Funktionen ermöglichen dem Programmierer die Modularisierung des Programms.
Funktionen ermöglichen es, komplizierte Programme in kleine Blöcke aufzuteilen, die sich leichter schreiben, lesen und warten lassen. Wir kennen bereits die Funktion main und haben printf aus der Standardbibliothek verwendet. Wir können natürlich auch unsere eigenen Funktionen und Header-Dateien erstellen. Eine Funktion hat das folgende Layout:
Rückgabetyp Funktionsname (ggf. Argumentenliste)
{
lokale Erklärungen;
Aussagen;
Rückgabewert zurückgeben;
}
Wenn der Rückgabetyp weggelassen wird, verwendet C standardmäßig int. Der Rückgabewert muss vom deklarierten Typ sein. Alle innerhalb von Funktionen deklarierten Variablen werden lokale Variablen genannt, da sie nur in der Funktion bekannt sind, für die sie definiert wurden.
Some functions have a parameter list that provides a communication method between the function, and the module that called the function. The parameters are also local variables, in that they are not available outside of the function. The programs covered so far all have main, which is a function.
A function may simply perform a task without returning any value, in which case it has the following layout:
void function-name ( argument list if necessary )
{
local-declarations ;
statements;
}
Arguments are always passed by value in C function calls. This means that local copies of the values of the arguments are passed to the routines. Any change made to the arguments internally in the function is made only to the local copies of the arguments.
In order to change or define an argument in the argument list, this argument must be passed as an address. You use regular variables if the function does not change the values of those arguments. You MUST use pointers if the function changes the values of those arguments.
Let us learn with examples:
#include <stdio.h>
void exchange ( int *a, int *b )
{
int temp;
temp = *a;
*a = *b;
*b = temp;
printf(" From function exchange: ");
printf("a = %d, b = %d\n", *a, *b);
}
void main()
{
int a, b;
a = 5;
b = 7;
printf("From main: a = %d, b = %d\n", a, b);
exchange(&a, &b);
printf("Back in main: ");
printf("a = %d, b = %d\n", a, b);
}
And the output of this program will be displayed as follows:
From main: a = 5, b = 7
From function exchange: a = 7, b = 5
Back in main: a = 7, b = 5
Let us see another example. The following example uses a function called square which writes the square of the numbers between 1 and 10.
#include <stdio.h>
int square(int x); /* Function prototype */
int main()
{
int counter;
for (counter=1; counter<=10; counter++)
printf("Square of %d is %d\n", counter, square(counter));
return 0;
}
/* Define the function 'square' */
int square(int x)
{
return x * x;
}
The output of this program will be displayed as follows:
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
Square of 6 is 36
Square of 7 is 49
Square of 8 is 64
Square of 9 is 81
Square of 10 is 100
Der Funktionsprototyp square deklariert eine Funktion, die einen Integer-Parameter annimmt und einen Integer zurückgibt. Wenn der Compiler im Hauptprogramm den Funktionsaufruf von square erreicht, kann er den Funktionsaufruf anhand der Funktionsdefinition überprüfen.
Wenn das Programm die Zeile erreicht, die die Funktion square aufruft, springt es zur Funktion und führt diese aus, bevor es seinen Weg durch das Hauptprogramm fortsetzt. Programme, die keinen Rückgabetyp haben, sollten mit void deklariert werden. Daher können Parameter für die Funktion per Wert oder Referenz übergeben werden.
Eine rekursive Funktion ist eine Funktion, die sich selbst aufruft. Und dieser Vorgang wird Rekursion genannt.
Übergabe von Wertfunktionen
Die Parameter der Quadratfunktion im vorherigen Beispiel werden als Wert übergeben. Dies bedeutet, dass nur eine Kopie der Variable an die Funktion übergeben wurde. Änderungen am Wert werden nicht an die aufrufende Funktion zurückgespiegelt.
Das folgende Beispiel verwendet die Wertübergabe und ändert den Wert des übergebenen Parameters, was keine Auswirkung auf die aufrufende Funktion hat. Die Funktion count_down wurde als void deklariert, da es keinen Rückgabetyp gibt.
#include <stdio.h>
void count_down(int x);
int main()
{
int-Zähler;
für (Zähler=1; Zähler<=10; Zähler++)
count_down(Zähler);
gebe 0 zurück;
}
void count_down(int x)
{
int-Zähler;
für (Zähler = x; Zähler > 0; Zähler--)
{
printf("%d ", x);
X--;
}
setchar('\n');
}
Die Ausgabe des Programms wird wie folgt angezeigt:
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
6 5 4 3 2 1
7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
Sehen wir uns zum besseren Verständnis ein weiteres Beispiel für C Pass By Value an. Das folgende Beispiel wandelt eine vom Benutzer eingegebene Zahl zwischen 1 und 30.000 in Wörter um.
#include <stdio.h>
: void do_units(int num);
void do_tens(int num);
void do_teens(int num);
int main()
{
int num, Rest;
Tun
{
printf("Geben Sie eine Zahl zwischen 1 und 30.000 ein: ");
scanf("%d", &num);
} während (Zahl < 1 || Zahl > 30000);
Rest = Zahl;
printf("%d in Wörtern = ", num);
do_tens(Rest/1000);
wenn (Zahl >= 1000)
printf("Tausend ");
Rest % = 1000;
do_units(Rest/100);
wenn (Rest >= 100)
{
printf("hundert ");
}
wenn (Zahl > 100 und Zahl%100 > 0)
printf("und ");
Rest %=100;
do_tens(Rest);
setchar('\n');
gebe 0 zurück;
}
void do_units(int num)
{
Schalter(Nummer)
{
Fall 1:
printf("eins");
brechen;
Fall 2:
printf("zwei ");
brechen;
Fall 3:
printf("drei ");
brechen;
Fall 4:
printf("vier ");
brechen;
Fall 5:
printf("fünf ");
brechen;
Fall 6:
printf("sechs ");
brechen;
Fall 7:
printf("sieben ");
brechen;
Fall 8:
printf("acht ");
brechen;
Fall 9:
printf("neun ");
}
}
void do_tens(int num)
{
Schalter (Zahl/10)
{
Fall 1:
do_teens(Nummer);
brechen;
Fall 2:
printf("zwanzig ");
brechen;
Fall 3:
printf("dreißig ");
brechen;
Fall 4:
printf("vierzig ");
brechen;
Fall 5:
printf("fünfzig ");
brechen;
Fall 6:
printf("sechzig ");
brechen;
Fall 7:
printf("siebzig ");
brechen;
Fall 8:
printf("achtzig ");
brechen;
Fall 9:
printf("neunzig ");
}
wenn (num/10 != 1)
do_units(Anzahl %10);
}
void do_teens(int num)
{
Schalter(Nummer)
{
Fall 10:
printf("zehn ");
brechen;
Fall 11:
printf("elf ");
brechen;
Fall 12:
printf("zwölf ");
brechen;
Fall 13:
printf("dreizehn ");
brechen;
Fall 14:
printf("vierzehn ");
brechen;
Fall 15:
printf("fünfzehn ");
brechen;
Fall 16:
printf("sechzehn ");
brechen;
Fall 17:
printf("siebzehn ");
brechen;
Fall 18:
printf("achtzehn ");
brechen;
Fall 19:
printf("neunzehn ");
}
}
und die Ausgabe des Programms sieht wie folgt aus:
Geben Sie eine Zahl zwischen 1 und 30.000 ein: 12345
12345 in Worten = zwölftausenddreihundertfünfundvierzig
Aufruf per Referenz
Um eine Funktion per Referenz aufzurufen, übergeben Sie statt der Variablen selbst die Adresse der Variablen. Die Adresse der Variablen kann mithilfe des Operators & abgerufen werden. Im Folgenden wird eine Swap-Funktion aufgerufen, die die Adresse der Variablen statt der tatsächlichen Werte übergibt.
tauschen(&x, &y);
Dereferenzierung
Unser aktuelles Problem besteht darin, dass der Funktion „swap“ die Adresse und nicht die Variable übergeben wurde. Daher müssen wir die Variablen dereferenzieren, damit wir zum Vertauschen die tatsächlichen Werte und nicht die Adressen der Variablen betrachten.
Dereferenzierung wird in C durch die Verwendung der Zeigernotation (*) erreicht. Einfach ausgedrückt bedeutet dies, dass vor jeder Variable ein * gesetzt wird, bevor sie verwendet wird, damit auf den Wert der Variablen und nicht auf ihre Adresse verwiesen wird. Das folgende Programm veranschaulicht die Übergabe per Referenz zum Vertauschen zweier Werte.
#include <stdio.h>
: void swap(int *x, int *y);
int main()
{
int x=6, y=10;
printf("Vor dem Funktionstausch war x = %d und y =
%d\n\n", x, y);
tauschen(&x, &y);
printf("Nach dem Funktionstausch ist x = %d und y =
%d\n\n", x, y);
gebe 0 zurück;
}
void swap(int *x, int *y)
{
temp = *x;
*x = *y;
*y = Temperatur;
}
Sehen wir uns die Ausgabe des Programms an:
Vor dem Funktionstausch war x = 6 und y = 10
Nach dem Funktionstausch ist x = 10 und y = 6
Funktionen können rekursiv sein, d. h. eine Funktion kann sich selbst aufrufen. Jeder Selbstaufruf erfordert, dass der aktuelle Zustand der Funktion auf den Stapel übertragen wird. Es ist wichtig, sich daran zu erinnern, da es leicht zu einem Stapelüberlauf kommen kann, d. h. der Stapel hat nicht mehr genügend Platz, um weitere Daten zu speichern.
Das folgende Beispiel berechnet die Fakultät einer Zahl mithilfe einer Rekursion. Eine Fakultät ist eine Zahl, die mit jeder anderen Ganzzahl unter sich multipliziert wird, bis hinunter zu 1. Die Fakultät der Zahl 6 lautet beispielsweise:
Fakultät 6 = 6 * 5 * 4 * 3 * 2 * 1
Daher beträgt die Fakultät von 6 720. Aus dem obigen Beispiel ist ersichtlich, dass Fakultät 6 = 6 * Fakultät 5. Ebenso gilt Fakultät 5 = 5 * Fakultät 4 und so weiter.
Im Folgenden finden Sie die allgemeine Regel zur Berechnung von Fakultätszahlen.
Fakultät(n) = n * Fakultät(n-1)
Die obige Regel endet, wenn n = 1, da die Fakultät von 1 1 ist. Versuchen wir, dies anhand eines Beispiels besser zu verstehen:
#include <stdio.h>
lange int Fakultät(int Num);
int main()
{
int-num;
lange int f;
printf("Geben Sie eine Zahl ein: ");
scanf("%d", &num);
f = Fakultät(Zahl);
printf("Fakultät von %d ist %ld\n", num, f);
gebe 0 zurück;
}
lange int Fakultät(int Num)
{
wenn (Zahl == 1)
Rückgabe 1;
anders
gibt Num * Fakultät (Num-1) zurück;
}
Sehen wir uns die Ausgabe der Ausführung dieses Programms an:
Geben Sie eine Zahl ein: 7
Fakultät von 7 ist 5040
Speicherzuweisung in C
Der C-Compiler verfügt über eine Speicherzuweisungsbibliothek, die in malloc.h definiert ist. Speicher wird mithilfe der Funktion malloc reserviert und gibt einen Zeiger auf die Adresse zurück. Sie nimmt einen Parameter an, nämlich die erforderliche Speichergröße in Bytes.
Das folgende Beispiel reserviert Speicherplatz für die Zeichenfolge „Hallo Welt“.
ptr = (char *)malloc(strlen("Hallo Welt") + 1);
Das zusätzliche Byte ist erforderlich, um das Zeichenfolgenabschlusszeichen „\0“ zu berücksichtigen. (char *) wird als Cast bezeichnet und erzwingt, dass der Rückgabetyp char * ist.
Da Datentypen unterschiedliche Größen haben und malloc den Speicherplatz in Bytes zurückgibt, empfiehlt es sich aus Portabilitätsgründen, beim Angeben einer zuzuweisenden Größe den Operator sizeof zu verwenden.
Das folgende Beispiel liest eine Zeichenfolge in den Zeichenarraypuffer ein, weist dann die genaue erforderliche Speichermenge zu und kopiert sie in eine Variable namens „ptr“.
#include <string.h>
#include <malloc.h>
int main()
{
char *ptr, Puffer[80];
printf("Geben Sie eine Zeichenfolge ein: ");
bekommt (Puffer);
ptr = (char *)malloc((strlen(Puffer) + 1) *
Größe von (Zeichen));
strcpy(ptr, Puffer);
printf("Sie haben eingegeben: %s\n", ptr);
gebe 0 zurück;
}
Die Ausgabe des Programms sieht wie folgt aus:
Geben Sie eine Zeichenfolge ein: Indien ist das Beste.
Sie haben eingegeben: Indien ist das Beste
Neuzuweisung von Speicher
Beim Programmieren kommt es oft vor, dass Sie Speicher neu zuweisen möchten. Dies geschieht mit der Funktion realloc. Die Funktion realloc verwendet zwei Parameter: die Basisadresse des Speichers, dessen Größe Sie ändern möchten, und die Menge an Speicherplatz, die Sie reservieren möchten. Anschließend gibt sie einen Zeiger auf die Basisadresse zurück.
Angenommen, wir haben Speicherplatz für einen Zeiger namens „msg“ reserviert und möchten den Speicherplatz neu zuweisen, und zwar auf die Menge an Speicherplatz, die er bereits belegt, plus die Länge einer anderen Zeichenfolge. Dann könnten wir Folgendes verwenden.
msg = (char *)realloc(msg, (strlen(msg) + strlen(Puffer) + 1)*Größevon(char));
Das folgende Programm veranschaulicht die Verwendung von malloc, realloc und free. Der Benutzer gibt eine Reihe von Zeichenfolgen ein, die miteinander verbunden werden. Das Programm beendet das Lesen von Zeichenfolgen, wenn eine leere Zeichenfolge eingegeben wird.
#include <string.h>
#include <malloc.h>
int main()
{
Zeichenpuffer[80], *msg;
int erstesMal=0;
Tun
{
printf("\nGeben Sie einen Satz ein: ");
bekommt (Puffer);
wenn (!erstesZeit)
{
msg = (char *)malloc((strlen(Puffer) + 1) *
Größe von (Zeichen));
strcpy(msg, Puffer);
erstesMal = 1;
}
anders
{
msg = (char *)realloc(msg, (strlen(msg) +
strlen(Puffer) + 1) * Größe von(Zeichen));
strcat(msg, Puffer);
}
setzt (Nachricht);
} während(strcmp(Puffer, ""));
frei(Nachricht);
gebe 0 zurück;
}
Die Ausgabe des Programms sieht wie folgt aus:
Geben Sie einen Satz ein: Es war einmal
Es war einmal
Geben Sie einen Satz ein: Es war einmal ein König
Es war einmal ein König
Geben Sie einen Satz ein: Der König war Es war einmal ein König Der König war
Geben Sie einen Satz ein:
Es war einmal ein König, der König war
Speicher freigeben
Wenn Sie den zugewiesenen Speicher nicht mehr benötigen, sollten Sie nie vergessen, ihn freizugeben, da dadurch Ressourcen freigegeben und die Geschwindigkeit verbessert wird. Um zugewiesenen Speicher freizugeben, verwenden Sie die Funktion „free“.
frei(ptr);
Bauwerke
Neben den grundlegenden Datentypen verfügt C über einen Strukturmechanismus, der es Ihnen ermöglicht, miteinander in Beziehung stehende Datenelemente unter einem gemeinsamen Namen zu gruppieren. Dies wird allgemein als benutzerdefinierter Typ bezeichnet.
Das Schlüsselwort struct startet die Strukturdefinition und ein Tag gibt der Struktur einen eindeutigen Namen. Die der Struktur hinzugefügten Datentypen und Variablennamen sind Mitglieder der Struktur. Das Ergebnis ist eine Strukturvorlage, die als Typbezeichner verwendet werden kann. Das Folgende ist eine Struktur mit einem Monatstag.
Strukturmonat
{
Zeichenname[10];
Zeichenabkürzung[4];
int Tage;
};
Ein Strukturtyp wird normalerweise am Anfang einer Datei mithilfe einer typedef-Anweisung definiert. typedef definiert und benennt einen neuen Typ und ermöglicht so dessen Verwendung im gesamten Programm. typedef steht in einer Datei normalerweise direkt nach den Anweisungen #define und #include.
Das Schlüsselwort typedef kann verwendet werden, um ein Wort zu definieren, das auf die Struktur verweist, anstatt das Schlüsselwort struct mit dem Namen der Struktur anzugeben. Es ist üblich, den typedef in Großbuchstaben zu benennen. Hier sind die Beispiele für Strukturdefinitionen.
typedef-Struktur {
Zeichenname[64];
char natürlich[128];
int Alter;
int Jahr;
} Student;
Dadurch wird ein neuer Typ „Student“ definiert. Variablen vom Typ „Student“ können wie folgt deklariert werden.
Schüler st_rec;
Beachten Sie, wie ähnlich dies der Deklaration eines int oder float ist. Der Variablenname ist st_rec, er hat Mitglieder namens name, course, age und year. Ähnlich,
typedef-Strukturelement
{
Char-Daten;
Strukturelement *nächstes;
} STAPELELEMENT;
Eine Variable vom benutzerdefinierten Typ struct element kann nun wie folgt deklariert werden.
STACKELEMENT *Stapel;
Betrachten Sie die folgende Struktur:
Strukturstudent
{
Zeichen *Name;
int. Klasse;
};
Ein Zeiger auf die Struktur „student“ kann wie folgt definiert werden.
Struktur Student *hnc;
Beim Zugriff auf einen Zeiger auf eine Struktur wird anstelle des Punktoperators der Memberzeigeroperator -> verwendet. Um einer Struktur eine Klasse hinzuzufügen,
s.Note = 50;
Sie können der Struktur beispielsweise wie folgt eine Note zuweisen.
s->Note = 50;
Wenn Sie möchten, dass die in einer Funktion an übergebenen Parametern vorgenommenen Änderungen dauerhaft bleiben, müssen Sie wie bei den Basisdatentypen die Übergabe per Referenz (die Adresse übergeben) durchführen. Der Mechanismus ist genau der gleiche wie bei den Basisdatentypen. Übergeben Sie die Adresse und verweisen Sie mithilfe der Zeigernotation auf die Variable.
Nachdem Sie die Struktur definiert haben, können Sie eine Instanz davon deklarieren und den Mitgliedern mithilfe der Punktnotation Werte zuweisen. Das folgende Beispiel veranschaulicht die Verwendung der Monatsstruktur.
#include <stdio.h>
#include <string.h>
Strukturmonat
{
Zeichenname[10];
Zeichenabkürzung[4];
int Tage;
};
int main()
{
Struktur Monat m;
strcpy(m.name, "Januar");
strcpy(m.abbreviation, "Jan");
m.Tage = 31;
printf("%s wird als %s abgekürzt und hat %d Tage\n", m.name, m.abbreviation, m.days);
gebe 0 zurück;
}
Die Ausgabe des Programms sieht wie folgt aus:
Januar wird als Jan abgekürzt und hat 31 Tage
Alle ANSI C-Compiler ermöglichen es Ihnen, eine Struktur einer anderen zuzuweisen, indem Sie eine mitgliederweise Kopie durchführen. Wenn wir Monatsstrukturen namens m1 und m2 hätten, könnten wir die Werte von m1 bis m2 folgendermaßen zuweisen:
- Struktur mit Zeigermitgliedern.
- Struktur wird initialisiert.
- Übergeben einer Struktur an eine Funktion.
- Zeiger und Strukturen.
Strukturen mit Zeigermitgliedern in C
Das Speichern von Zeichenfolgen in einem Array fester Größe ist eine ineffiziente Nutzung des Speichers. Ein effizienterer Ansatz wäre die Verwendung von Zeigern. Zeiger werden in Strukturen genauso verwendet wie in normalen Zeigerdefinitionen. Sehen wir uns ein Beispiel an:
#include <string.h>
#include <malloc.h>
Strukturmonat
{
Zeichen *Name;
char *Abkürzung;
int Tage;
};
int main()
{
Struktur Monat m;
m.name = (char *)malloc((strlen("Januar")+1) *
Größe von (Zeichen));
strcpy(m.name, "Januar");
m.Abkürzung = (char *)malloc((strlen("Jan")+1) *
Größe von (Zeichen));
strcpy(m.abbreviation, "Jan");
m.Tage = 31;
printf("%s wird als %s abgekürzt und hat %d Tage\n",
m.Name, m.Abkürzung, m.Tage);
gebe 0 zurück;
}
Die Ausgabe des Programms sieht wie folgt aus:
Januar wird als Jan abgekürzt und hat 31 Tage
Strukturinitialisierer in C
Um einen Satz von Anfangswerten für die Struktur bereitzustellen, können Initialisierer zur Deklarationsanweisung hinzugefügt werden. Da Monate bei 1 beginnen, Arrays in C jedoch bei Null, wurde im folgenden Beispiel ein zusätzliches Element an Position Null verwendet, das als Junk bezeichnet wird.
#include <stdio.h>
#include <string.h>
Strukturmonat
{
Zeichen *Name;
char *Abkürzung;
int Tage;
} Monatsdetails[] =
{
"Müll", "Müll", 0,
"Januar", "Jan", 31,
"Februar", "Feb", 28,
"März", "Mar", 31,
"April", "Apr", 30,
"Mai", "Mai", 31,
"Juni", "Jun", 30,
"Juli", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"Oktober", "Okt", 31,
"November", "Nov", 30,
"Dezember", "Dez", 31
};
int main()
{
int-Zähler;
für (Zähler=1; Zähler<=12; Zähler++)
printf("%s wird als %s abgekürzt und hat %d Tage\n",
Monat_Details[Zähler].Name,
Monatsdetails[Zähler].Abkürzung,
Monat_Details[Zähler].Tage);
gebe 0 zurück;
}
Die Ausgabe wird wie folgt angezeigt:
Januar wird als Jan abgekürzt und hat 31 Tage
Februar wird als Feb abgekürzt und hat 28 Tage
März wird als Mar abgekürzt und hat 31 Tage
April wird als Apr abgekürzt und hat 30 Tage
Der Mai wird als Mai abgekürzt und hat 31 Tage
Juni wird als Jun abgekürzt und hat 30 Tage
Juli wird als Jul abgekürzt und hat 31 Tage
August wird als Aug abgekürzt und hat 31 Tage
September wird als Sep abgekürzt und hat 30 Tage
Oktober wird als Okt abgekürzt und hat 31 Tage
November wird als Nov abgekürzt und hat 30 Tage
Dezember wird als Dez abgekürzt und hat 31 Tage
Übergeben von Strukturen an Funktionen in C
Strukturen können wie alle grundlegenden Datentypen als Parameter an eine Funktion übergeben werden. Im folgenden Beispiel wird eine Struktur namens date verwendet, die an eine isLeapYear-Funktion übergeben wird, um zu bestimmen, ob das Jahr ein Schaltjahr ist.
Normalerweise würden Sie nur den Tageswert übergeben, aber die gesamte Struktur wird übergeben, um die Übergabe von Strukturen an Funktionen zu veranschaulichen.
#include <stdio.h>
#include <string.h>
Strukturmonat
{
Zeichen *Name;
char *Abkürzung;
int Tage;
} Monatsdetails[] =
{
"Müll", "Müll", 0,
"Januar", "Jan", 31,
"Februar", "Feb", 28,
"März", "Mar", 31,
"April", "Apr", 30,
"Mai", "Mai", 31,
"Juni", "Jun", 30,
"Juli", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"Oktober", "Okt", 31,
"November", "Nov", 30,
"Dezember", "Dez", 31
};
Strukturdatum
{
int Tag;
int Monat;
int Jahr;
};
int istSchaltjahr(Struktur date d);
int main()
{
Strukturdatum d;
printf("Geben Sie das Datum ein (zB: 11.11.1980): ");
scanf("%d/%d/%d", &d.Tag, &d.Monat, &d.Jahr);
printf("Das Datum %d %s %d ist ", d.day,
Monatsdetails[Tag.Monat].Name, Tag.Jahr);
wenn (istSchaltjahr(d) == 0)
printf("nicht ");
puts("ein Schaltjahr");
gebe 0 zurück;
}
int istSchaltjahr(Struktur date d)
{
wenn ((d.year % 4 == 0 und d.year % 100 != 0) ||
d.Jahr % 400 == 0)
Rückgabe 1;
gebe 0 zurück;
}
Die Ausführung des Programms erfolgt wie folgt:
Geben Sie das Datum ein (z. B.: 11/11/1980): 9/12/1980
Das Datum 9. Dezember 1980 ist ein Schaltjahr
Das folgende Beispiel weist dynamisch ein Array von Strukturen zu, um die Namen und Noten der Studenten zu speichern. Die Noten werden dem Benutzer dann in aufsteigender Reihenfolge angezeigt.
#include <string.h>
#include <malloc.h>
Struktur Student
{
Zeichen *Name;
int. Klasse;
};
void swap(Struktur Student *x, Struktur Student *y);
int main()
{
Struktur Student *Gruppe;
Zeichenpuffer[80];
int unecht;
int innen, außen;
int-Zähler, AnzahlStudenten;
printf("Wie viele Studenten sind in der Gruppe: ");
scanf("%d", &numStudenten);
Gruppe = (Struktur Student *)malloc(AnzahlStudenten *
sizeof(Struktur Student));
für (Zähler=0; Zähler<Anz.Studenten; Zähler++)
{
unecht = getchar();
printf("Geben Sie den Namen des Studenten ein: ");
bekommt (Puffer);
Gruppe[Zähler].Name = (char *)malloc((strlen(Puffer)+1) * Größe von(char));
strcpy(Gruppe[Zähler].Name, Puffer);
printf("Note eingeben: ");
scanf("%d", &group[counter].grade);
}
für (äußere=0; äußere<AnzahlStudenten; äußere++)
für (inner=0; inner<äußer; inner++)
wenn (Gruppe[äußere].Klasse <
Gruppe[innere].Klasse)
tauschen(&Gruppe[außen], &Gruppe[innen]);
puts("Die Gruppe in aufsteigender Reihenfolge der Noten ...");
für (Zähler=0; Zähler<Anz.Studenten; Zähler++)
printf("%s hat die Note %d erreicht \n”,
Gruppe[Zähler].Name,
Gruppe[Zähler].Klasse);
gebe 0 zurück;
}
void swap(Struktur Student *x, Struktur Student *y)
{
Struktur Student Temp;
temp.name = (char *)malloc((strlen(x->name)+1) *
Größe von (Zeichen));
strcpy(temp.name, x->name);
temp.grade = x->grade;
x->Note = y->Note;
x->name = (char *)malloc((strlen(y->name)+1) *
Größe von (Zeichen));
strcpy(x->Name, y->Name);
y->Klasse = temp.Klasse;
y->name = (char *)malloc((strlen(temp.name)+1) *
Größe von (Zeichen));
strcpy(y->name, temp.name);
}
Die Ausführung der Ausgabe erfolgt wie folgt:
Wie viele Schüler sind in der Gruppe: 4
Geben Sie den Namen des Schülers ein: Anuraaj
Geben Sie die Klasse ein: 7
Geben Sie den Namen des Schülers ein: Honey
Geben Sie die Klasse ein: 2 Geben Sie
den Namen des Schülers ein: Meetushi
Geben Sie die Klasse ein: 1
Geben Sie den Namen des Schülers ein: Deepti
Geben Sie die Klasse ein: 4
Die Gruppe in aufsteigender Notenfolge ...
Meetushi erreichte die Note 1.
Honey erreichte die Note 2.
Deepti erreichte die Note 4.
Anuraaj erreichte die Note 7
Union
Eine Union ermöglicht es Ihnen, dieselben Daten mit unterschiedlichen Typen anzuzeigen oder dieselben Daten mit unterschiedlichen Namen zu verwenden. Unions sind Strukturen ähnlich. Eine Union wird auf dieselbe Weise deklariert und verwendet wie eine Struktur.
Eine Union unterscheidet sich von einer Struktur dadurch, dass immer nur eines ihrer Mitglieder gleichzeitig verwendet werden kann. Der Grund dafür ist einfach. Alle Mitglieder einer Union belegen denselben Speicherbereich. Sie liegen übereinander.
Unions werden auf die gleiche Weise wie Strukturen definiert und deklariert. Der einzige Unterschied in den Deklarationen besteht darin, dass das Schlüsselwort union anstelle von struct verwendet wird. Um eine einfache Union einer char-Variablen und einer Integer-Variablen zu definieren, würden Sie Folgendes schreiben:
Union geteilt {
Zeichen c;
int ich;
};
Diese Union, geteilt, kann verwendet werden, um Instanzen einer Union zu erstellen, die entweder einen Zeichenwert c oder einen ganzzahligen Wert i enthalten können. Dies ist eine ODER-Bedingung. Im Gegensatz zu einer Struktur, die beide Werte enthalten würde, kann die Union nur einen Wert gleichzeitig enthalten.
Eine Union kann bei ihrer Deklaration initialisiert werden. Da immer nur ein Mitglied gleichzeitig verwendet werden kann und nur eines initialisiert werden kann. Um Verwirrung zu vermeiden, kann nur das erste Mitglied der Union initialisiert werden. Der folgende Code zeigt eine Instanz der gemeinsamen Union, die deklariert und initialisiert wird:
Union geteilte generische_Variable = {`@'};
Beachten Sie, dass die Vereinigung „generic_variable“ genauso initialisiert wurde, wie das erste Mitglied einer Struktur initialisiert würde.
Einzelne Union-Mitglieder können mithilfe des Mitgliedsoperators (.) auf die gleiche Weise wie Strukturmitglieder verwendet werden. Beim Zugriff auf Union-Mitglieder gibt es jedoch einen wichtigen Unterschied.
Es sollte immer nur auf ein Union-Mitglied gleichzeitig zugegriffen werden. Da eine Union ihre Mitglieder übereinander speichert, ist es wichtig, immer nur auf ein Mitglied gleichzeitig zuzugreifen.
Das Schlüsselwort union
union tag {
Gewerkschaftsmitglied(er);
/* hier können weitere Anweisungen eingefügt werden */
}Beispiel;
Das Schlüsselwort union wird zum Deklarieren von Unions verwendet. Eine Union ist eine Sammlung von einer oder mehreren Variablen (union_members), die unter einem einzigen Namen gruppiert wurden. Darüber hinaus belegt jedes dieser Union-Mitglieder denselben Speicherbereich.
Das Schlüsselwort union kennzeichnet den Anfang einer Union-Definition. Darauf folgt ein Tag, der den Namen der Union angibt. Dem Tag folgen die Union-Mitglieder in Klammern.
Eine Instanz, die eigentliche Deklaration einer Union, kann ebenfalls definiert werden. Wenn Sie die Struktur ohne die Instanz definieren, ist sie nur eine Vorlage, die später in einem Programm zum Deklarieren von Strukturen verwendet werden kann. Das folgende Format einer Vorlage ist:
union tag {
Gewerkschaftsmitglied(er);
/* hier können weitere Anweisungen eingefügt werden */
};
Um die Vorlage zu verwenden, verwenden Sie das folgende Format: Union-Tag-Instanz;
Um dieses Format zu verwenden, müssen Sie zuvor eine Vereinigung mit dem angegebenen Tag deklariert haben.
/* Deklariere eine Union-Vorlage namens Tag */
union tag {
int-num;
Saiblingsalpen;
}
/* Verwenden Sie die Union-Vorlage */
Union-Tag gemischte_Variable;
/* Eine Union und eine Instanz gemeinsam deklarieren */
union generischer_typ_tag {
Zeichen c;
int ich;
Schwimmer f;
Doppel-D;
} generisch;
/* Initialisiere eine Vereinigung. */
union Datumstag {
char vollständiges_Datum[9];
Struktur part_date_tag {
char Monat[2];
Zeichenunterbrechungswert1;
char Tag[2];
Zeichenunterbrechungswert2;
Zeichenjahr[2];
} Teildatum;
}Datum = {"09/12/80"};
Lassen Sie es uns anhand von Beispielen besser verstehen:
#include <stdio.h>
int main()
{
Union
{
int value; /* Dies ist der erste Teil der Vereinigung */
Struktur
{
char first; /* Diese beiden Werte sind der zweite Teil davon */
Zeichen Sekunde;
} Hälfte;
} Nummer;
langer Index;
für (Index = 12; Index < 300000L; Index += 35231L)
{
Zahl.Wert = Index;
printf("%8x %6x %6x\n", Zahl.Wert,
Zahl.Hälfte.Zuerst,
Zahl.halbe.Sekunde);
}
gebe 0 zurück;
}
Die Ausgabe des Programms wird wie folgt angezeigt:
cc 0
89ab fab ff89
134a 4a 13
9ce9 ffe9 ff9c
2688 ff88 26
b027 27 ffb0
39c6 ffc6 39
c365 65 ffc3
4d04 4 4d
Eine praktische Anwendung einer Union bei der Datenwiederherstellung
Sehen wir uns nun eine praktische Anwendung von Union in der Datenwiederherstellung an. Nehmen wir ein kleines Beispiel. Das folgende Programm ist das kleine Modell eines Programms zum Scannen fehlerhafter Sektoren für ein Diskettenlaufwerk (a:), es ist jedoch kein vollständiges Modell einer Software zum Scannen fehlerhafter Sektoren.
Lassen Sie uns das Programm untersuchen:
#include<dos.h>
#include<conio.h>
int main()
{
int rp, Kopf, Spur, Sektor, Status;
char *buf;
Gewerkschaftsreglemente rein, raus;
Struktur SREGS s;
clrscr();
/* Setzen Sie das Festplattensystem zurück, um die Festplatte zu initialisieren */
printf("\n Das Festplattensystem wird zurückgesetzt...");
für (rp=0;rp<=2;rp++)
{
in.h.ah = 0;
in.h.dl = 0x00;
int86(0x13,&ein,&aus);
}
printf("\n\n\n Die Festplatte wird jetzt auf fehlerhafte Sektoren getestet...");
/* nach fehlerhaften Sektoren suchen */
für (Spur=0;Spur<=79;Spur++)
{
für (Kopf=0;Kopf<=1;Kopf++)
{
für (Sektor = 1; Sektor <= 18; Sektor ++)
{
in.h.ah = 0x04;
in.h.al = 1;
in.h.dl = 0x00;
in.h.ch = Spur;
in.h.dh = Kopf;
in.h.cl = Sektor;
in.x.bx = FP_OFF(buf);
s.es = FP_SEG(buf);
int86x(0x13,&ein,&aus,&s);
wenn(out.x.cflag)
{
Status=aus.h.ah;
printf("\n Spur: %d Kopf: %d Sektor: %d Status ==0x%X",Spur,Kopf,Sektor,Status);
}
}
}
}
printf("\n\n\nFertig");
gebe 0 zurück;
}
Sehen wir uns nun an, wie die Ausgabe aussieht, wenn die Diskette fehlerhafte Sektoren enthält:
Das Festplattensystem wird zurückgesetzt...
Jetzt wird die Festplatte auf fehlerhafte Sektoren getestet …
Spur:0 Kopf:0 Sektor:4 Status ==0xA
Spur:0 Kopf:0 Sektor:5 Status ==0xA
Spur:1 Kopf:0 Sektor:4 Status ==0xA
Spur:1 Kopf:0 Sektor:5 Status ==0xA
Spur:1 Kopf:0 Sektor:6 Status ==0xA
Spur:1 Kopf:0 Sektor:7 Status ==0xA
Spur:1 Kopf:0 Sektor:8 Status ==0xA
Spur:1 Kopf:0 Sektor:11 Status ==0xA
Spur:1 Kopf:0 Sektor:12 Status ==0xA
Spur:1 Kopf:0 Sektor:13 Status ==0xA
Spur:1 Kopf:0 Sektor:14 Status ==0xA
Spur:1 Kopf:0 Sektor:15 Status ==0xA
Spur:1 Kopf:0 Sektor:16 Status ==0xA
Spur:1 Kopf:0 Sektor:17 Status ==0xA
Spur:1 Kopf:0 Sektor:18 Status ==0xA
Spur:1 Kopf:1 Sektor:5 Status ==0xA
Spur:1 Kopf:1 Sektor:6 Status ==0xA
Spur:1 Kopf:1 Sektor:7 Status ==0xA
Spur:1 Kopf:1 Sektor:8 Status ==0xA
Spur:1 Kopf:1 Sektor:9 Status ==0xA
Spur:1 Kopf:1 Sektor:10 Status ==0xA
Spur:1 Kopf:1 Sektor:11 Status ==0xA
Spur:1 Kopf:1 Sektor:12 Status ==0xA
Spur:1 Kopf:1 Sektor:13 Status ==0xA
Spur:1 Kopf:1 Sektor:14 Status ==0xA
Spur:1 Kopf:1 Sektor:15 Status ==0xA
Spur:1 Kopf:1 Sektor:16 Status ==0xA
Spur:1 Kopf:1 Sektor:17 Status ==0xA
Spur:1 Kopf:1 Sektor:18 Status ==0xA
Spur:2 Kopf:0 Sektor:4 Status ==0xA
Spur:2 Kopf:0 Sektor:5 Status ==0xA
Spur:14 Kopf:0 Sektor:6 Status ==0xA
Erledigt
Es kann ein wenig schwierig sein, die in diesem Programm verwendeten Funktionen und Interrupts zum Überprüfen der Festplatte auf fehlerhafte Sektoren und zum Zurücksetzen des Festplattensystems usw. zu verstehen, aber keine Sorge, wir werden all diese Dinge später in den nächsten Kapiteln im BIOS und in den Abschnitten zur Interrupt-Programmierung lernen.
Dateiverwaltung in C
Der Dateizugriff in C erfolgt durch die Verknüpfung eines Streams mit einer Datei. C kommuniziert mit Dateien über einen neuen Datentyp namens Dateizeiger. Dieser Typ ist in stdio.h definiert und wird als FILE * geschrieben. Ein Dateizeiger namens output_file wird in einer Anweisung wie folgt deklariert:
DATEI *Ausgabedatei;
Die Dateimodi der fopen-Funktion
Ihr Programm muss eine Datei öffnen, bevor es darauf zugreifen kann. Dies geschieht mit der Funktion fopen, die den erforderlichen Dateizeiger zurückgibt. Wenn die Datei aus irgendeinem Grund nicht geöffnet werden kann, wird der Wert NULL zurückgegeben. Normalerweise verwenden Sie fopen wie folgt
wenn ((Ausgabedatei = fopen("Ausgabedatei", "w")) == NULL)
fprintf(stderr, "%s konnte nicht geöffnet werden\n",
"Ausgabedatei");
fopen verwendet zwei Argumente, beide sind Zeichenfolgen. Das erste ist der Name der zu öffnenden Datei, das zweite ist ein Zugriffszeichen, normalerweise eines der Zeichen r, a oder w usw. Dateien können in einer Reihe von Modi geöffnet werden, wie in der folgenden Tabelle gezeigt.
Dateimodi |
R |
Öffnen Sie eine Textdatei zum Lesen. |
In |
Erstellt eine Textdatei zum Schreiben. Wenn die Datei existiert, wird sie überschrieben. |
A |
Öffnen Sie eine Textdatei im Anfügemodus. Der Text wird am Ende der Datei hinzugefügt. |
rb |
Öffnen Sie eine Binärdatei zum Lesen. |
wb |
Erstellt eine Binärdatei zum Schreiben. Wenn die Datei existiert, wird sie überschrieben. |
ab |
Öffnet eine Binärdatei im Anfügemodus. Daten werden am Ende der Datei hinzugefügt. |
r+ |
Öffnen Sie eine Textdatei zum Lesen und Schreiben. |
in+ |
Erstellt eine Textdatei zum Lesen und Schreiben. Wenn die Datei existiert, wird sie überschrieben. |
eine+ |
Öffnen Sie am Ende eine Textdatei zum Lesen und Schreiben. |
r+b oder rb+ |
Öffnen Sie die Binärdatei zum Lesen und Schreiben. |
w+b oder wb+ |
Erstellt eine Binärdatei zum Lesen und Schreiben. Wenn die Datei existiert, wird sie überschrieben. |
a+b oder ab+ |
Öffnen Sie am Ende eine Textdatei zum Lesen und Schreiben. |
Die Aktualisierungsmodi werden mit den Funktionen fseek, fsetpos und rewind verwendet. Die Funktion fopen gibt einen Dateizeiger zurück oder NULL, wenn ein Fehler auftritt.
Das folgende Beispiel öffnet die Datei tarun.txt im schreibgeschützten Modus. Es ist gute Programmierpraxis, zu testen, ob die Datei vorhanden ist.
wenn ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Datei kann nicht geöffnet werden");
gebe 0 zurück;
}
Dateien schließen
Dateien werden mit der Funktion fclose geschlossen. Die Syntax lautet wie folgt:
fclose(in);
Lesen von Dateien
Mit der Funktion feof wird das Dateiende geprüft. Mit den Funktionen fgetc, fscanf und fgets werden Daten aus der Datei gelesen.
Das folgende Beispiel listet den Inhalt einer Datei auf dem Bildschirm auf und verwendet fgetc, um die Datei Zeichen für Zeichen zu lesen.
#include <stdio.h>
int main()
{
DATEI *in;
int-Schlüssel;
wenn ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Datei kann nicht geöffnet werden");
gebe 0 zurück;
}
während (!feof(in))
{
Schlüssel = fgetc(in);
/* Das letzte gelesene Zeichen ist die Dateiende-Markierung, also drucken Sie es nicht aus */
wenn (!feof(in))
putchar(Schlüssel);
}
fclose(in);
gebe 0 zurück;
}
Mit der Funktion fscanf können verschiedene Datentypen aus der Datei gelesen werden, wie im folgenden Beispiel, vorausgesetzt, die Daten in der Datei liegen im Format der mit fscanf verwendeten Formatzeichenfolge vor.
fscanf(in, "%d/%d/%d", &Tag, &Monat, &Jahr);
Mit der Funktion fgets wird eine Anzahl von Zeichen aus einer Datei gelesen. stdin ist der Standardeingabedateistream und mit der Funktion fgets kann die Eingabe gesteuert werden.
Schreiben in Dateien
Daten können mit fputc und fprintf in die Datei geschrieben werden. Im folgenden Beispiel werden die Funktionen fgetc und fputc verwendet, um eine Kopie einer Textdatei zu erstellen.
#include <stdio.h>
int main()
{
DATEI *ein, *aus;
int-Schlüssel;
wenn ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Datei kann nicht geöffnet werden");
gebe 0 zurück;
}
out = fopen("kopie.txt", "w");
während (!feof(in))
{
Schlüssel = fgetc(in);
wenn (!feof(in))
fputc(Schlüssel, raus);
}
fclose(in);
fschließen(aus);
gebe 0 zurück;
}
Mit der Funktion fprintf können formatierte Daten in eine Datei geschrieben werden.
fprintf(out, "Datum: %02d/%02d/%02d\n",
Tag, Monat, Jahr);
Befehlszeilenargumente mit C
Die ANSI-C-Definition zum Deklarieren der main()-Funktion lautet entweder:
int main() oder int main(int argc, char **argv)
Die zweite Version ermöglicht die Übergabe von Argumenten über die Befehlszeile. Der Parameter argc ist ein Argumentzähler und enthält die Anzahl der von der Befehlszeile übergebenen Parameter. Der Parameter argv ist der Argumentvektor, ein Array von Zeigern auf Zeichenfolgen, die die tatsächlich übergebenen Parameter darstellen.
Das folgende Beispiel erlaubt die Übergabe beliebiger Argumente über die Kommandozeile und gibt diese aus. argv[0] ist das eigentliche Programm. Das Programm muss über eine Eingabeaufforderung ausgeführt werden.
#include <stdio.h>
int main(int argc, char **argv)
{
int-Zähler;
puts("Die Argumente für das Programm sind:");
für (Zähler=0; Zähler<argc; Zähler++)
setzt (argv[Zähler]);
gebe 0 zurück;
}
Wenn der Programmname count.c wäre, könnte es wie folgt von der Kommandozeile aus aufgerufen werden.
Anzahl 3
oder
Anzahl 7
oder
Anzahl 192 usw.
Im nächsten Beispiel werden die Dateibehandlungsroutinen verwendet, um eine Textdatei in eine neue Datei zu kopieren. Das Befehlszeilenargument könnte beispielsweise wie folgt aufgerufen werden:
txtcpy eins.txt zwei.txt
#include <stdio.h>
int main(int argc, char **argv)
{
DATEI *ein, *aus;
int-Schlüssel;
wenn (argc < 3)
{
puts("Verwendung: txtcpy Quelle Ziel\n");
puts("Die Quelle muss eine vorhandene Datei sein");
puts("Wenn die Zieldatei existiert, wird sie
überschrieben");
gebe 0 zurück;
}
wenn ((in = fopen(argv[1], "r")) == NULL)
{
puts("Die zu kopierende Datei kann nicht geöffnet werden");
gebe 0 zurück;
}
wenn ((out = fopen(argv[2], "w")) == NULL)
{
puts("Die Ausgabedatei konnte nicht geöffnet werden");
gebe 0 zurück;
}
während (!feof(in))
{
Schlüssel = fgetc(in);
wenn (!feof(in))
fputc(Schlüssel, raus);
}
fclose(in);
fschließen(aus);
gebe 0 zurück;
}
Bitweise Manipulatoren
Auf Hardwareebene werden Daten als Binärzahlen dargestellt. Die binäre Darstellung der Zahl 59 ist 111011. Bit 0 ist das niederwertigste Bit, und in diesem Fall ist Bit 5 das höchstwertige Bit.
Jeder Bitsatz wird als 2 hoch dem Bitsatz berechnet. Bitweise Operatoren ermöglichen die Manipulation von Integer-Variablen auf Bitebene. Nachfolgend sehen Sie die binäre Darstellung der Zahl 59.
Binärdarstellung der Zahl 59 |
Bit 5 4 3 2 1 0 |
2 Leistung n 32 16 8 4 2 1 |
Satz 1 1 1 0 1 1 |
Mit drei Bits ist es möglich, die Zahlen 0 bis 7 darzustellen. Die folgende Tabelle zeigt die Zahlen 0 bis 7 in ihrer Binärform.
Binärziffern |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
In der folgenden Tabelle sind die bitweisen Operatoren aufgeführt, die zur Manipulation von Binärzahlen verwendet werden können.
Binärziffern |
und |
Bitweises UND |
| |
Bitweises ODER |
^ |
Bitweises exklusives ODER |
~ |
Bitweises Komplement |
<< |
Bitweises Verschieben nach links |
>> |
Bitweises Verschieben nach rechts |
Bitweises UND
Das bitweise UND ist nur dann wahr, wenn beide Bits gesetzt sind. Das folgende Beispiel zeigt das Ergebnis eines bitweisen UND für die Zahlen 23 und 12.
10111 (23)
01100 (12) UND
____________________
00100 (Ergebnis = 4) |
Sie können einen Maskenwert verwenden, um zu prüfen, ob bestimmte Bits gesetzt wurden. Wenn wir prüfen möchten, ob die Bits 1 und 3 gesetzt sind, könnten wir die Zahl mit 10 maskieren (dem Wert der Bits 1 und 3) und das Ergebnis anhand der Maske testen.
#include <stdio.h>
int main()
{
int num, Maske = 10;
printf("Geben Sie eine Zahl ein: ");
scanf("%d", &num);
wenn ((Num & Maske) == Maske)
puts("Bits 1 und 3 sind gesetzt");
anders
puts("Bits 1 und 3 sind nicht gesetzt");
gebe 0 zurück;
}
Bitweises ODER
Das bitweise ODER ist wahr, wenn eines der beiden Bits gesetzt ist. Im Folgenden wird das Ergebnis eines bitweisen ODER für die Zahlen 23 und 12 gezeigt.
10111 (23)
01100 (12) ODER
______________________
11111 (Ergebnis = 31) |
Sie können eine Maske verwenden, um sicherzustellen, dass ein oder mehrere Bits gesetzt wurden. Das folgende Beispiel stellt sicher, dass Bit 2 gesetzt ist.
#include <stdio.h>
int main()
{
int num, Maske = 4;
printf("Geben Sie eine Zahl ein: ");
scanf("%d", &num);
Num |= Maske;
printf("Nachdem sichergestellt wurde, dass Bit 2 gesetzt ist: %d\n", num);
gebe 0 zurück;
}
Bitweises exklusives ODER
Das bitweise Exklusiv-ODER ist Wahr, wenn eines der Bits gesetzt ist, aber nicht beide. Im Folgenden wird das Ergebnis eines bitweisen Exklusiv-ODER für die Zahlen 23 und 12 gezeigt.
10111 (23)
01100 (12) Exklusiv-ODER (XOR)
_____________________________
11011 (Ergebnis = 27) |
Das Exklusiv-ODER hat einige interessante Eigenschaften. Wenn Sie eine Zahl mit sich selbst exklusiv ODER verknüpfen, wird sie selbst auf Null gesetzt, da die Nullen Null bleiben und die Einsen nicht beide gesetzt werden können und daher auf Null gesetzt werden.
Wenn Sie also eine Zahl mit einer anderen Zahl exklusiv ODER verknüpfen und dann das Ergebnis mit der anderen Zahl erneut exklusiv ODER verknüpfen, ist das Ergebnis die ursprüngliche Zahl. Sie können dies mit den im obigen Beispiel verwendeten Zahlen versuchen.
23 ODER 12 = 27
27 ODER 12 = 23
27 ODER 23 = 12
Diese Funktion kann zur Verschlüsselung verwendet werden. Das folgende Programm verwendet einen Verschlüsselungsschlüssel von 23, um die Eigenschaft einer vom Benutzer eingegebenen Nummer zu veranschaulichen.
#include <stdio.h>
int main()
{
int num, Schlüssel = 23;
printf("Geben Sie eine Zahl ein: ");
scanf("%d", &num);
num ^= Taste;
printf("Exklusiv-ODER mit %d ergibt %d\n", key, num);
num ^= Taste;
printf("Exklusiv-ODER mit %d ergibt %d\n", key, num);
gebe 0 zurück;
}
Bitweises Kompliment
Das bitweise Kompliment ist ein Einerkomplementoperator, der das Bit ein- oder ausschaltet. Wenn es 1 ist, wird es auf 0 gesetzt, wenn es 0 ist, wird es auf 1 gesetzt.
#include <stdio.h>
int main()
{
int num = 0xFFFF;
printf("Das Komplement von %X ist %X\n", num, ~num);
gebe 0 zurück;
}
Bitweises Verschieben nach links
Der Operator „Bitweises Verschieben nach links“ verschiebt die Zahl nach links. Die höchstwertigen Bits gehen verloren, wenn sich die Zahl nach links verschiebt, und die frei gewordenen niederwertigsten Bits sind Null. Im Folgenden sehen Sie die binäre Darstellung von 43.
0101011 (Dezimalzahl 43)
Durch das Verschieben der Bits nach links verlieren wir das höchstwertige Bit (in diesem Fall eine Null) und die Zahl wird mit einer Null am niederwertigsten Bit aufgefüllt. Die resultierende Zahl ist wie folgt:
1010110 (Dezimalzahl 86)
Bitweises Verschieben nach rechts
Der Operator „Bitweise Verschiebung nach rechts“ verschiebt die Zahl nach rechts. Die frei gewordenen höchstwertigen Bits werden mit Nullen versehen, und die frei gewordenen niederwertigsten Bits gehen verloren. Nachfolgend sehen Sie die binäre Darstellung der Zahl 43.
0101011 (Dezimalzahl 43)
Durch das Verschieben der Bits nach rechts verlieren wir das niederwertigste Bit (in diesem Fall eine Eins) und die Zahl wird mit einer Null am höchstwertigen Bit aufgefüllt. Die resultierende Zahl ist wie folgt:
0010101 (Dezimalzahl 21)
Das folgende Programm verwendet die bitweise Rechtsverschiebung und die bitweise UND-Funktion, um eine Zahl als 16-Bit-Binärzahl anzuzeigen. Die Zahl wird sukzessive von 16 nach rechts bis auf 0 verschoben und mit 1 bitweise UND-verknüpft, um zu sehen, ob das Bit gesetzt ist. Eine alternative Methode wäre die Verwendung sukzessiver Masken mit dem bitweisen ODER-Operator.
#include <stdio.h>
int main()
{
int-Zähler, Num;
printf("Geben Sie eine Zahl ein: ");
scanf("%d", &num);
printf("%d ist binär: ", num);
für (Zähler=15; Zähler>=0; Zähler--)
printf("%d", (num >> Zähler) & 1);
setchar('\n');
gebe 0 zurück;
}
Funktionen für Binär-Dezimal-Konvertierungen
Die beiden nächsten Funktionen dienen der Konvertierung von Binärzahlen in Dezimalzahlen und von Dezimalzahlen in Binärzahlen. Die nächste Funktion zur Konvertierung einer Dezimalzahl in die entsprechende Binärzahl unterstützt Binärzahlen bis zu 32 Bit. Sie können diese oder das zuvor angegebene Programm zur Konvertierung entsprechend Ihren Anforderungen verwenden.
Funktion zur Umwandlung von Dezimal in Binär:
void Dezimal_zu_Binär(void)
{
int-Eingabe =0;
int ich;
int-Anzahl = 0;
int binary [32]; /* 32 Bit, MAXIMAL 32 Elemente */
printf ("Geben Sie die Dezimalzahl ein, die umgewandelt werden soll in
Binär :");
scanf ("%d", &Eingabe);
Tun
{
i = input%2; /* MOD 2 um 1 oder 0 zu erhalten*/
binär[Anzahl] = i; /* Elemente in das binäre Array laden */
Eingabe = Eingabe/2; /* Teilen Sie die Eingabe durch 2, um sie binär zu dekrementieren */
count++; /* Zähle, wie viele Elemente benötigt werden*/
}während (Eingabe > 0);
/* Binärziffern umkehren und ausgeben */
printf ("Binäre Darstellung ist: ");
Tun
{
printf ("%d", binär[Anzahl - 1]);
zählen--;
} während (Anzahl > 0);
printf ("\n");
}
Funktion zur Umwandlung von Binär in Dezimal:
Die folgende Funktion dient zum Umwandeln einer beliebigen Binärzahl in die entsprechende Dezimalzahl:
void Binär_zu_Dezimal(void)
{
char binaryhold[512];
char *binär;
} i = 0;
int dec = 0;
int z;
printf ("Bitte geben Sie die Binärziffern ein.\n");
printf ("Binärziffern sind entweder nur 0 oder 1");
printf ("Binärer Eintrag: ");
binär = erhält(binärhalten);
i=strlen(binär);
für (z=0; z<i; ++z)
{
dec=dec*2+(binary[z]=='1'? 1:0); /* wenn Binary[z] ist
gleich 1,
dann 1 sonst 0 */
}
printf ("\n");
printf ("Dezimalwert von %s ist %d",
binär, dez);
printf ("\n");
}
Debuggen und Testen
Syntaxfehler
Syntax bezieht sich auf die Grammatik, Struktur und Reihenfolge der Elemente in einer Anweisung. Ein Syntaxfehler tritt auf, wenn wir gegen die Regeln verstoßen, z. B. wenn wir vergessen, eine Anweisung mit einem Semikolon zu beenden. Wenn Sie das Programm kompilieren, erstellt der Compiler eine Liste aller Syntaxfehler, die auftreten können.
Ein guter Compiler gibt die Liste mit einer Beschreibung des Fehlers aus und bietet möglicherweise eine mögliche Lösung. Das Beheben der Fehler kann dazu führen, dass beim erneuten Kompilieren weitere Fehler angezeigt werden. Der Grund dafür ist, dass die vorherigen Fehler die Struktur des Programms geändert haben, sodass weitere Fehler während der ursprünglichen Kompilierung unterdrückt wurden.
Ebenso kann ein einziger Fehler mehrere Fehler zur Folge haben. Versuchen Sie, ein Semikolon an das Ende der Hauptfunktion eines Programms zu setzen, das sich korrekt kompilieren und ausführen lässt. Wenn Sie es erneut kompilieren, erhalten Sie eine riesige Liste von Fehlern, und dabei handelt es sich nur um ein falsch platziertes Semikolon.
Neben Syntaxfehlern können Compiler auch Warnungen ausgeben. Eine Warnung ist kein Fehler, kann aber bei der Ausführung Ihres Programms zu Problemen führen. Wenn Sie beispielsweise einer Gleitkommazahl mit doppelter Genauigkeit eine Gleitkommazahl mit einfacher Genauigkeit zuweisen, kann dies zu einem Genauigkeitsverlust führen. Dies ist kein Syntaxfehler, kann aber zu Problemen führen. In diesem speziellen Beispiel könnten Sie Ihre Absicht zeigen, indem Sie die Variable in den entsprechenden Datentyp umwandeln.
Betrachten Sie das folgende Beispiel, in dem x eine Gleitkommazahl mit einfacher Genauigkeit und y eine Gleitkommazahl mit doppelter Genauigkeit ist. y wird während der Zuweisung explizit in eine Gleitkommazahl umgewandelt, wodurch sämtliche Compilerwarnungen vermieden würden.
x = (Fließkomma)y;
Logische Fehler
Logikfehler treten auf, wenn ein Fehler in der Logik vorliegt. Sie könnten beispielsweise testen, ob eine Zahl kleiner als 4 und größer als 8 ist. Das kann unmöglich immer der Fall sein, aber wenn es syntaktisch korrekt ist, wird das Programm erfolgreich kompiliert. Betrachten Sie das folgende Beispiel:
wenn (x < 4 && x > 8)
puts("Wird nie passieren!");
Die Syntax ist korrekt, das Programm wird also kompiliert, die Puts-Anweisung wird jedoch nie ausgedruckt, da der Wert von x nicht gleichzeitig kleiner als vier und größer als acht sein kann.
Die meisten Logikfehler werden beim ersten Testen des Programms entdeckt. Wenn es sich nicht wie erwartet verhält, prüfen Sie die logischen Anweisungen genauer und korrigieren sie. Dies gilt nur für offensichtliche Logikfehler. Je größer das Programm ist und je mehr Pfade es durch das Programm gibt, desto schwieriger wird es zu überprüfen, ob sich das Programm wie erwartet verhält.
Testen
Im Softwareentwicklungsprozess können in jedem Stadium der Entwicklung Fehler auftreten. Dies liegt daran, dass die Überprüfungsmethoden früherer Phasen der Softwareentwicklung manuell sind. Daher enthält der während der Codierungsaktivität entwickelte Code wahrscheinlich neben während der Codierungsaktivität eingeführten Fehlern auch einige Anforderungs- und Designfehler. Während des Tests wird das zu testende Programm mit einer Reihe von Testfällen ausgeführt und die Ausgabe des Programms für die Testfälle wird ausgewertet, um festzustellen, ob die Programmierung wie erwartet funktioniert.
Beim Testen handelt es sich also um den Prozess der Analyse eines Softwareelements, um die Unterschiede zwischen vorhandenen und erforderlichen Bedingungen (d. h. Fehler) zu erkennen und die Funktionen der Softwareelemente zu bewerten. Beim Testen handelt es sich also um den Prozess der Analyse eines Programms mit der Absicht, Fehler zu finden.
Einige Testprinzipien
- Durch Tests kann nicht die Abwesenheit von Mängeln nachgewiesen werden, sondern nur deren Vorhandensein.
- Je früher ein Fehler passiert, desto kostspieliger ist er.
- Je später ein Fehler erkannt wird, desto kostspieliger ist er.
Lassen Sie uns nun einige Testtechniken besprechen:
White-Box-Tests
Beim White-Box-Test werden alle Pfade durch das Programm mit jedem möglichen Wert getestet. Dieser Ansatz erfordert ein gewisses Wissen darüber, wie sich das Programm verhalten soll. Wenn Ihr Programm beispielsweise einen ganzzahligen Wert zwischen 1 und 50 akzeptiert, würde ein White-Box-Test das Programm mit allen 50 Werten testen, um sicherzustellen, dass es für jeden Wert korrekt ist. Anschließend würde er jeden anderen möglichen Wert testen, den eine ganze Zahl annehmen kann, und prüfen, ob es sich wie erwartet verhält. Angesichts der Anzahl der Datenelemente, die ein typisches Programm haben kann, machen die möglichen Permutationen den White-Box-Test für große Programme äußerst schwierig.
White-Box-Tests können auf sicherheitskritische Funktionen eines großen Programms angewendet werden, und ein Großteil der übrigen Funktionen wird mit Black-Box-Tests getestet, die weiter unten erläutert werden. Aufgrund der Anzahl der Permutationen werden White-Box-Tests normalerweise mithilfe eines Test-Harnesses durchgeführt, bei dem Wertebereiche schnell über ein spezielles Programm in das Programm eingespeist werden und Ausnahmen vom erwarteten Verhalten protokolliert werden. White-Box-Tests werden manchmal auch als Struktur-, Clear- oder Open-Box-Tests bezeichnet.
Black-Box-Tests
Black-Box-Tests ähneln White-Box-Tests, nur dass nicht jeder mögliche Wert getestet wird, sondern ausgewählte Werte. Bei dieser Art von Test kennt der Tester die Eingaben und die erwarteten Ergebnisse, aber nicht unbedingt, wie das Programm zu ihnen gekommen ist. Black-Box-Tests werden manchmal auch als Funktionstests bezeichnet.
Testfälle für Black-Box-Tests werden normalerweise unmittelbar nach Fertigstellung der Programmspezifikationen entwickelt. Testfälle basieren auf Äquivalenzklassen.
Äquivalenzklassen
Für jede Eingabe definiert eine Äquivalenzklasse gültige und ungültige Zustände. Beim Definieren von Äquivalenzklassen müssen normalerweise drei Szenarios berücksichtigt werden.
Wenn die Eingabedaten einen Bereich oder einen bestimmten Wert angeben, werden ein gültiger und zwei ungültige Zustände definiert. Wenn eine Zahl beispielsweise zwischen 1 und 20 liegen muss, liegt der gültige Zustand zwischen 1 und 20, es gibt einen ungültigen Zustand für Werte kleiner als 1 und einen ungültigen Zustand größer als 20.
Wenn die Eingabedaten einen Bereich oder einen bestimmten Wert ausschließen, werden zwei gültige Zustände und ein ungültiger Zustand definiert. Wenn eine Zahl beispielsweise nicht zwischen 1 und 20 liegen darf, wären gültige Zustände kleiner als 1 und größer als 20 und ungültige Zustände wären zwischen 1 und 20.
Wenn die Eingabe einen Booleschen Wert angibt, gibt es nur zwei Zustände: einen gültigen und einen ungültigen.
Grenzwertanalyse
Bei der Grenzwertanalyse werden nur die Werte an der Grenze der Eingabedaten berücksichtigt. Beispielsweise könnten im Fall einer Zahl von 1 bis 20 die Testfälle 1, 20, 0 und 21 sein. Die Idee ist, dass, wenn das Programm mit diesen Werten wie erwartet funktioniert, auch andere Werte wie erwartet funktionieren.
Die folgende Tabelle bietet einen Überblick über typische Grenzen, die Sie möglicherweise definieren möchten.
Prüfbereiche |
Eingabetyp |
Prüfwerte |
Reichweite |
- x[Untergrenze]-1
- x[Untergrenze]
- x[Obergrenze]
- x[Obergrenze]+1
|
Boolescher Wert |
|
Einen Testplan entwickeln
Definieren Sie Äquivalenzklassen und legen Sie die Grenzen für jede Klasse fest. Nachdem Sie die Grenzen für die Klasse definiert haben, schreiben Sie eine Liste mit akzeptablen und inakzeptablen Werten an der Grenze und geben Sie an, wie das erwartete Verhalten aussehen sollte. Der Tester kann dann das Programm mit den Grenzwerten ausführen und angeben, was passiert ist, als der Grenzwert mit dem erforderlichen Ergebnis getestet wurde.
Unten sehen Sie einen typischen Testplan zur Validierung eingegebener Altersangaben. Die zulässigen Werte liegen zwischen 10 und 110.
Äquivalenzklasse |
Gültig |
Falsch |
Zwischen 10 und 110 |
> 110 |
|
< 10 |
Nachdem wir unsere Äquivalenzklasse bestimmt haben, können wir nun einen Testplan für das Alter entwickeln.
Testplan |
Wert |
Zustand |
Erwartetes Ergebnis |
Tatsächliches Ergebnis |
10 |
Gültig |
Fahren Sie mit der Ausführung fort, um den Namen zu erhalten |
|
110 |
Gültig |
Fahren Sie mit der Ausführung fort, um den Namen zu erhalten |
|
9 |
Falsch |
Frage nochmal nach dem Alter |
|
111 |
Falsch |
Frage nochmal nach dem Alter |
|
Die Spalte „Tatsächliches Ergebnis“ bleibt leer, da sie während des Tests ausgefüllt wird. Wenn das Ergebnis wie erwartet ist, wird die Spalte mit einem Häkchen versehen. Wenn nicht, sollten Sie einen Kommentar eingeben, der angibt, was passiert ist.