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 Bilanz 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 von 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:
- Erstellen Sie das aktive Verzeichnis Ihrer C-Programme und starten Sie Ihren Editor. Hierfür können Sie jeden beliebigen Texteditor verwenden, aber die meisten C-Compiler wie Borlands Turbo C++ verfügen über eine integrierte Entwicklungsumgebung (IDE), mit der Sie Ihre Programme in einer praktischen Umgebung eingeben, kompilieren und verknüpfen können.
- Schreiben und speichern Sie den Quellcode. Sie sollten die Datei CPROG.C nennen.
- Kompilieren und verknüpfen Sie CPROG.C. Führen Sie den entsprechenden Befehl aus, der in den Handbüchern Ihres Compilers angegeben ist. Sie sollten eine Meldung erhalten, dass keine Fehler oder Warnungen aufgetreten sind.
- Überprüfen Sie die Compilermeldungen. Wenn Sie keine Fehler oder Warnungen erhalten, sollte alles in Ordnung sein. Wenn beim Eintippen des Programms ein Fehler auftritt, erkennt der Compiler ihn und zeigt eine Fehlermeldung an. Korrigieren Sie den in der Fehlermeldung angezeigten Fehler.
- Ihr erstes C-Programm sollte nun kompiliert und laufbereit sein. Wenn Sie eine Verzeichnisliste aller Dateien mit dem Namen CPROG anzeigen, erhalten Sie die vier Dateien mit unterschiedlichen Erweiterungen, die wie folgt beschrieben werden:
- CPROG.C, die Quellcodedatei
- CPROG.BAK, die Sicherungsdatei der Quelldatei, die Sie mit dem Editor erstellt haben
- CPROG.OBJ, enthält den Objektcode für CPROG.C
- CPROG.EXE, das ausführbare Programm, das beim Kompilieren und Verknüpfen von CPROG.C erstellt wurde
- Um CPROG.EXE auszuführen, geben Sie einfach cprog ein. Auf dem Bildschirm wird die Meldung „Dies ist ein C-Programm“ angezeigt.
Betrachten wir nun das folgende Programm:
/* Erstes Programm zum Erlernen von C */ // 1
// 2
#include <stdio.h> // 3
// 4
Haupt() // 5
{
// 6
printf("Dies ist ein C-Programm\n"); // 7
// 8
return 0; // 9
} // 10
Wenn Sie dieses Programm kompilieren, zeigt der Compiler eine Meldung ähnlich der folgenden an:
cprog.c(8) : Fehler: `;' erwartet
Lassen Sie uns diese Fehlermeldung in Teile aufteilen. cprog.c ist der Name der Datei, in der der Fehler gefunden wurde. (8) ist die Zeilennummer, in der der Fehler gefunden wurde. Fehler: „;“ erwartet ist eine Beschreibung des Fehlers.
Diese Meldung ist recht informativ und teilt Ihnen mit, dass der Compiler in Zeile 8 von CPROG.C ein Semikolon erwartet hatte, aber keins gefunden hat. Sie wissen jedoch, dass das Semikolon in Zeile 7 tatsächlich weggelassen wurde, sodass eine Diskrepanz besteht.
Warum meldet der Compiler in Zeile 8 einen Fehler, obwohl in Zeile 7 tatsächlich ein Semikolon ausgelassen wurde? Die Antwort liegt darin, dass C Dinge wie Zeilenumbrüche nicht berücksichtigt. Das Semikolon, das nach der printf()-Anweisung gehört, hätte in die nächste Zeile gesetzt werden können, obwohl dies in der Praxis schlechte Programmierung wäre.
Erst beim nächsten Befehl (Return) in Zeile 8 ist der Compiler sicher, dass das Semikolon fehlt. Daher meldet der Compiler, dass der Fehler in Zeile 8 steht.
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 Funktion div() 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 |
'\”' |
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 erlauben 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.
Dieses spezielle Programm ist wirklich ein Durcheinander, aber es ist ein gutes Beispiel dafür, warum Softwareentwickler versuchen, die Verwendung der goto-Anweisung so weit wie möglich zu vermeiden. Die einzige Stelle in diesem Programm, an der es sinnvoll ist, goto zu verwenden, ist dort, wo das Programm mit einem Sprung aus den drei verschachtelten Schleifen herausspringt. In diesem Fall wäre es ziemlich chaotisch, eine Variable einzurichten und nacheinander aus jeder der drei verschachtelten Schleifen herauszuspringen, aber eine goto-Anweisung bringt Sie auf sehr präzise Weise aus allen drei heraus.
Manche Leute meinen, die goto-Anweisung sollte unter keinen Umständen verwendet werden, aber das ist engstirniges Denken. Wenn es eine Stelle gibt, an der ein goto-Befehl eindeutig einen saubereren Kontrollfluss ermöglicht als eine andere Konstruktion, können Sie ihn trotzdem verwenden, da er auch im Rest des Programms auf Ihrem Monitor verwendet wird. Sehen wir uns das Beispiel an:
#include <stdio.h>
int main()
{
int Hund, Katze, Schwein;
gehe zu real_start;
irgendwo:
printf("Dies ist eine weitere Zeile im Chaos.\n");
gehe zu stopp_it;
/* der folgende Abschnitt ist der einzige Abschnitt mit einem verwendbaren goto */
real_start:
für (Hund = 1; Hund < 6; Hund = Hund + 1)
{
für (Katze = 1; Katze < 6; Katze = Katze + 1)
{
für(Schwein = 1; Schwein < 4; Schwein = Schwein + 1)
{
printf("Hund = %d Katze = %d Schwein = %d\n", Hund, Katze, Schwein);
wenn ((Hund + Katze + Schwein) > 8) gehe zu genug;
}
}
}
genug: printf("Das sind fürs Erste genug Tiere.\n");
/* dies ist das Ende des Abschnitts mit einer verwendbaren goto-Anweisung */
printf("\nDies ist die erste Zeile des Codes.\n");
gehe dorthin;
Wo:
printf("Dies ist die dritte Zeile des Codes.\n");
gehe irgendwohin;
Dort:
printf("Dies ist die zweite Zeile des Codes.\n");
gehe wohin;
hör auf damit:
printf("Dies ist die letzte Zeile dieses Durcheinanders.\n");
gebe 0 zurück;
}
Lassen Sie uns die angezeigten Ergebnisse sehen
Hund = 1 Katze = 1 Schwein = 1
Hund = 1 Katze = 1 Schwein = 2
Hund = 1 Katze = 1 Schwein = 3
Hund = 1 Katze = 2 Schwein = 1
Hund = 1 Katze = 2 Schwein = 2
Hund = 1 Katze = 2 Schwein = 3
Hund = 1 Katze = 3 Schwein = 1
Hund = 1 Katze = 3 Schwein = 2
Hund = 1 Katze = 3 Schwein = 3
Hund = 1 Katze = 4 Schwein = 1
Hund = 1 Katze = 4 Schwein = 2
Hund = 1 Katze = 4 Schwein = 3
Hund = 1 Katze = 5 Schwein = 1
Hund = 1 Katze = 5 Schwein = 2
Hund = 1 Katze = 5 Schwein = 3
Das sind vorerst genug Tiere.
Dies ist die erste Zeile des Codes.
Dies ist die zweite Zeile des Codes.
Dies ist die dritte Zeile des Codes.
Dies ist ein weiterer Aspekt des Chaos.
Dies ist die letzte Zeile dieses Durcheinanders.
Zeiger
Manchmal möchten wir wissen, wo sich eine Variable im Speicher befindet. Ein Zeiger enthält die Adresse einer Variablen, die einen bestimmten Wert hat. Beim Deklarieren eines Zeigers wird unmittelbar vor dem Zeigernamen ein Sternchen gesetzt.
Die Adresse des Speicherplatzes, an dem die Variable abgelegt ist, lässt sich ermitteln, indem man vor den Variablennamen ein Et-Zeichen setzt.
int num; /* Normale Integer-Variable */
int *numPtr; /* Zeiger auf eine Integer-Variable */
Das folgende Beispiel druckt den Variablenwert und die Adresse dieser Variablen im Speicher.
printf("Der Wert %d ist an der Adresse %X\n gespeichert", num, &num);
Um dem Zeiger numPtr die Adresse der Variablen num zuzuweisen, weisen Sie die Adresse der Variablen num wie im folgenden Beispiel zu:
numPtr = #
Um herauszufinden, was an der von numPtr angezeigten Adresse gespeichert ist, muss die Variable dereferenziert werden. Die Dereferenzierung erfolgt über das Sternchen, mit dem der Zeiger deklariert wurde.
printf("Der Wert %d ist an der Adresse %X\n gespeichert", *numPtr, numPtr);
Alle Variablen eines Programms befinden sich im Speicher. Die folgenden Anweisungen fordern den Compiler auf, auf einem 32-Bit-Computer 4 Byte Speicher für die Gleitkommavariable x zu reservieren und dann den Wert 6,5 darin abzulegen.
schweben x;
x = 6,5;
Da die Adresse einer beliebigen Variable im Speicher durch Platzieren des Operators & vor ihrem Namen ermittelt wird, ist &x die Adresse von x. C ermöglicht es uns, einen Schritt weiter zu gehen und eine Variable zu definieren, die als Zeiger bezeichnet wird und die Adresse anderer Variablen enthält. Wir können vielmehr sagen, dass der Zeiger auf eine andere Variable zeigt. Beispiel:
schweben x;
Gleitkommazahl*Pixel;
x = 6,5;
px = &x;
definiert px als Zeiger auf Objekte vom Typ float und setzt ihn gleich der Adresse von x. Somit bezieht sich *px auf den Wert von x:

Lassen Sie uns die folgenden Aussagen untersuchen:
int var_x;
int*ptrX;
wobei_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("Wert von x: %d", var_x);
Die erste Zeile veranlasst den Compiler, Speicherplatz für eine Ganzzahl zu reservieren. Die zweite Zeile weist den Compiler an, Speicherplatz für die Speicherung eines Zeigers zu reservieren.
Ein Zeiger ist ein Speicherort für eine Adresse. Die dritte Zeile sollte Sie an die scanf-Anweisungen erinnern. Der Adressoperator „&“ weist den Compiler an, an den Ort zu gehen, an dem var_x gespeichert ist, und dann die Adresse des Speicherorts an ptrX weiterzugeben.
Das Sternchen * vor einer Variable weist den Compiler an, den Zeiger zu dereferenzieren und in den Speicher zu wechseln. Dann können Sie Zuweisungen an die an diesem Ort gespeicherte Variable vornehmen. Sie können eine Variable referenzieren und über einen Zeiger auf ihre Daten zugreifen. Sehen wir uns ein Beispiel für Zeiger an:
/* Darstellung der Zeigerverwendung */
#include <stdio.h>
int main()
{
int-Index, *pt1, *pt2;
index = 39; /* beliebiger numerischer Wert */
pt1 = &index; /* die Adresse des 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-gesamt = 0;
für (i = 0; i < Größe; i++)
Gesamt + = Array [i];
Rendite (Gesamt);
}
Das folgende Programm erstellt einen String, greift auf einige darin enthaltene Daten zu und druckt ihn aus. Greifen Sie erneut mit Zeigern darauf zu und drucken Sie den String aus. Es sollte „Hi!“ und „012345678“ in unterschiedlichen Zeilen drucken. Sehen wir uns den Code des Programms an:
#include <stdio.h>
#define STR_LENGTH 10
void main()
{
char Str[STR_LENGTH];
char* pStr;
int ich;
Str[0] = 'H';
Str[1] = "i";
Str[2] = '!';
Str[3] = '\0'; // spezielles String-Endzeichen NULL
printf("Der String in Str ist: %s\n", Str);
pStr = &Str[0];
für (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("Der String in Str ist: %s\n", Str);
}
[] (eckige Klammern) werden verwendet, um das Array zu deklarieren. Die Programmzeile char Str[STR_LENGTH]; deklariert ein Array von zehn Zeichen. Dies sind zehn einzelne Zeichen, die alle zusammen im Speicher an derselben Stelle abgelegt werden. Sie sind alle über unseren Variablennamen Str zusammen mit einem [n] erreichbar, wobei n die Elementnummer ist.
Wenn Sie über Arrays sprechen, sollten Sie immer bedenken, dass, wenn C ein Array von zehn deklariert, die Elemente, auf die Sie zugreifen können, von 0 bis 9 nummeriert sind. Der Zugriff auf das erste Element entspricht dem Zugriff auf das 0. Element. Zählen Sie bei Arrays also immer von 0 bis zur Größe des Arrays - 1.
Beachten Sie als nächstes, dass wir die Buchstaben „Hi!“ in das Array einfügen, dann aber ein „\0“ einfügen. Sie fragen sich wahrscheinlich, was das ist. „\0“ steht für NULL und stellt das Ende einer Zeichenfolge dar. Alle Zeichenfolgen müssen mit diesem Sonderzeichen „\0“ enden. Wenn dies nicht der Fall ist und dann jemand printf für die Zeichenfolge aufruft, beginnt printf an der Speicherstelle Ihrer Zeichenfolge und setzt den Ausdruck fort, bis es auf „\0“ stößt. Dadurch haben Sie am Ende Ihrer Zeichenfolge einen Haufen Müll. Achten Sie also darauf, Ihre Zeichenfolgen ordnungsgemäß zu beenden.
Zeichen-Arrays
Eine Stringkonstante wie
„Ich bin eine Schnur“
ist ein Array von Zeichen. Es wird intern in C durch die ASCII-Zeichen in der Zeichenfolge dargestellt, also „I“, Leerzeichen, „a“, „m“ … oder die obige Zeichenfolge, und durch das spezielle Nullzeichen „\0“ abgeschlossen, damit Programme das Ende der Zeichenfolge finden können.
Um die Ausgabe von Code mit printf verständlicher zu machen, werden häufig Stringkonstanten verwendet:
printf("Hallo Welt\n");
printf("Der Wert von a ist: %f\n", a);
String-Konstanten können mit Variablen verknüpft werden. C bietet die Variable des Zeichentyps, die jeweils ein Zeichen (1 Byte) enthalten kann. Ein Zeichenstring wird in einem Array des Zeichentyps gespeichert, ein ASCII-Zeichen pro Speicherort.
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));
/* Weise der Variablen zufällige Werte zu */
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++)
{
/* Spalten anzeigen */
für (Spalte=0; Spalte<5; Spalte++)
printf("%d\t", x[Zeile][Spalte]);
setzechar('\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.
Die vollständige Definition einer Zeichenfolge ist eine Reihe von Daten vom Zeichentyp, die durch ein Nullzeichen ('\0') abgeschlossen werden.
Wenn C eine Datenzeichenfolge auf irgendeine Weise verwenden möchte, sei es, um sie mit einer anderen Zeichenfolge zu vergleichen, sie auszugeben, sie in eine andere Zeichenfolge zu kopieren oder was auch immer, sind die Funktionen so eingerichtet, dass sie das tun, wozu sie aufgerufen werden, bis ein Nullwert erkannt wird.
Es gibt keinen grundlegenden Datentyp für einen String in C. Stattdessen werden Strings in C als Array von Zeichen implementiert. Um beispielsweise einen Namen zu speichern, können Sie ein Zeichenarray deklarieren, das groß genug ist, um den Namen zu speichern, und dann die entsprechenden Bibliotheksfunktionen verwenden, um den Namen zu bearbeiten.
Das folgende Beispiel zeigt die vom Benutzer eingegebene Zeichenfolge auf dem Bildschirm an:
#include <stdio.h>
int main()
{
char name[80]; /* Erstelle ein Zeichen-Array
aufgerufener Name */
printf("Geben Sie Ihren Namen ein: ");
bekommt(Name);
printf("Der von Ihnen eingegebene Name war %s\n", name);
gebe 0 zurück;
}
Die Ausführung des Programms erfolgt:
Geben Sie Ihren Namen ein: Tarun Tyagi
Der von Ihnen eingegebene Name war Tarun Tyagi
Einige allgemeine String-Funktionen
Die Standardbibliothek string.h enthält viele nützliche Funktionen zur Manipulation von Zeichenfolgen. Einige der nützlichsten Funktionen sind hier als Beispiele aufgeführt.
Die strlen-Funktion
Die Funktion strlen wird verwendet, um die Länge eines Strings zu bestimmen. Lassen Sie uns die Verwendung von strlen anhand eines Beispiels lernen:
#include <stdio.h>
#include <string.h>
int main()
{
Zeichenname[80];
int Länge;
printf("Geben Sie Ihren Namen ein: ");
bekommt(Name);
Länge = strlen(Name);
printf("Ihr Name hat %d Zeichen\n", Länge);
gebe 0 zurück;
}
Und die Ausführung des Programms erfolgt wie folgt:
Geben Sie Ihren Namen ein: Tarun Subhash Tyagi
Ihr Name hat 19 Zeichen
Geben Sie Ihren Namen ein: Preeti Tarun
Ihr Name hat 12 Zeichen
Die strcpy-Funktion
Die Funktion strcpy wird verwendet, um einen String in einen anderen zu kopieren. Lassen Sie uns die Verwendung dieser Funktion anhand eines Beispiels lernen:
#include <stdio.h>
#include <string.h>
int main()
{
Zeichen zuerst[80];
char Sekunde[80];
printf("Geben Sie die erste Zeichenfolge ein: ");
bekommt (zuerst);
printf("Geben Sie die zweite Zeichenfolge ein: ");
bekommt (Sekunde);
printf("erste: %s, und zweite: %s Vor strcpy()\n "
, erster, zweiter);
strcpy(zweiter, erster);
printf("erste: %s, und zweite: %s Nach strcpy()\n",
erster, zweiter);
gebe 0 zurück;
}
und die Ausgabe des Programms sieht wie folgt aus:
Geben Sie zuerst den String ein: Tarun
Geben Sie zweiten String ein: Tyagi
zuerst: Tarun und zweitens: Tyagi Vor 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.
Einige Funktionen haben eine Parameterliste, die eine Kommunikationsmethode zwischen der Funktion und dem Modul bereitstellt, das die Funktion aufgerufen hat. Die Parameter sind auch lokale Variablen, da sie außerhalb der Funktion nicht verfügbar sind. Die bisher behandelten Programme haben alle main, was eine Funktion ist.
Eine Funktion kann einfach eine Aufgabe ausführen, ohne einen Wert zurückzugeben. In diesem Fall hat sie das folgende Layout:
void Funktionsname (Argumentliste wenn nötig)
{
lokale Deklarationen;
aussagen;
}
Argumente werden in C-Funktionsaufrufen immer als Wert übergeben. Dies bedeutet, dass lokale Kopien der Werte der Argumente an die Routinen übergeben werden. Jede Änderung, die intern in der Funktion an den Argumenten vorgenommen wird, wird nur an den lokalen Kopien der Argumente vorgenommen.
Um ein Argument in der Argumentliste zu ändern oder zu definieren, muss dieses Argument als Adresse übergeben werden. Sie verwenden reguläre Variablen, wenn die Funktion die Werte dieser Argumente nicht ändert. Sie MÜSSEN Zeiger verwenden, wenn die Funktion die Werte dieser Argumente ändert.
Lassen Sie uns anhand von Beispielen lernen:
#include <stdio.h>
ungültiger Austausch (int *a, int *b)
{
int-Temperatur;
temp = *a;
*a = *b;
*b = Temperatur;
printf("Aus Funktionsaustausch: ");
druckenf("a = %d, b = %d\n", *a, *b);
}
void main()
{
int a, b;
ein = 5;
b = 7;
printf("Von main: a = %d, b = %d\n", a, b);
Austausch(&a, &b);
printf("Zurück im Hauptmenü: ");
printf("a = %d, b = %d\n", a, b);
}
Die Ausgabe dieses Programms wird wie folgt angezeigt:
Von main: a = 5, b = 7
Aus Funktionsaustausch: a = 7, b = 5
Zurück im Hauptteil: a = 7, b = 5
Sehen wir uns ein weiteres Beispiel an. Das folgende Beispiel verwendet eine Funktion namens Quadrat, die das Quadrat der Zahlen zwischen 1 und 10 schreibt.
#include <stdio.h>
int square(int x); /* Funktionsprototyp */
int main()
{
int-Zähler;
für (Zähler=1; Zähler<=10; Zähler++)
printf("Das Quadrat von %d ist %d\n", Zähler, Quadrat(Zähler));
gebe 0 zurück;
}
/* Definieren Sie die Funktion 'Quadrat' */
int Quadrat(int x)
{
gebe x * x zurück;
}
Die Ausgabe dieses Programms wird wie folgt angezeigt:
Das Quadrat von 1 ist 1
Das Quadrat von 2 ist 4
Das Quadrat von 3 ist 9
Das Quadrat von 4 ist 16
Das Quadrat von 5 ist 25
Das Quadrat von 6 ist 36
Das Quadrat von 7 ist 49
Das Quadrat von 8 ist 64
Das Quadrat von 9 ist 81
Das Quadrat von 10 ist 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--;
}
setzechar('\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);
setzechar('\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:
Struktur Student
{
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 mit 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
The following example dynamically allocates an array of structures to store students names and grade. The grades are then displayed back to the user in ascending order.
#include <string.h>
#include <malloc.h>
struct student
{
char *name;
int grade;
};
void swap(struct student *x, struct student *y);
int main()
{
struct student *group;
char buffer[80];
int spurious;
int inner, outer;
int counter, numStudents;
printf("How many students are there in the group: ");
scanf("%d", &numStudents);
group = (struct student *)malloc(numStudents *
sizeof(struct student));
for (counter=0; counter<numStudents; counter++)
{
spurious = getchar();
printf("Enter the name of the student: ");
gets(buffer);
group[counter].name = (char *)malloc((strlen(buffer)+1) * sizeof(char));
strcpy(group[counter].name, buffer);
printf("Enter grade: ");
scanf("%d", &group[counter].grade);
}
for (outer=0; outer<numStudents; outer++)
for (inner=0; inner<outer; inner++)
if (group[outer].grade <
group[inner].grade)
swap(&group[outer], &group[inner]);
puts("The group in ascending order of grades ...");
for (counter=0; counter<numStudents; counter++)
printf("%s achieved Grade %d \n”,
group[counter].name,
group[counter].grade);
return 0;
}
void swap(struct student *x, struct student *y)
{
struct student temp;
temp.name = (char *)malloc((strlen(x->name)+1) *
sizeof(char));
strcpy(temp.name, x->name);
temp.grade = x->grade;
x->grade = y->grade;
x->name = (char *)malloc((strlen(y->name)+1) *
sizeof(char));
strcpy(x->name, y->name);
y->grade = temp.grade;
y->name = (char *)malloc((strlen(temp.name)+1) *
sizeof(char));
strcpy(y->name, temp.name);
}
The execution of the output will be as follows:
How many students are there in the group: 4
Enter the name of the student: Anuraaj
Enter grade: 7
Enter the name of the student: Honey
Enter grade: 2
Enter the name of the student: Meetushi
Enter grade: 1
Enter the name of the student: Deepti
Enter grade: 4
The group in ascending order of grades ...
Meetushi achieved Grade 1
Honey achieved Grade 2
Deepti achieved Grade 4
Anuraaj achieved Grade 7
Union
A union allows you a way to look at the same data with different types, or to use the same data with different names. Unions are similar to structures. A union is declared and used in the same ways that a structure is.
A union differs from a structure in that only one of its members can be used at a time. The reason for this is simple. All the members of a union occupy the same area of memory. They are laid on top of each other.
Unions are defined and declared in the same fashion as structures. The only difference in the declarations is that the keyword union is used instead of struct. To define a simple union of a char variable and an integer variable, you would write the following:
union shared {
char c;
int i;
};
This union, shared, can be used to create instances of a union that can hold either a character value c or an integer value i. This is an OR condition. Unlike a structure that would hold both values, the union can hold only one value at a time.
A union can be initialized on its declaration. Because only one member can be used at a time and only one can be initialized. To avoid confusion, only the first member of the union can be initialized. The following code shows an instance of the shared union being declared and initialized:
union shared generic_variable = {`@'};
Notice that the generic_variable union was initialized just as the first member of a structure would be initialized.
Individual union members can be used in the same way that structure members can be used by using the member operator (.). However, there is an important difference in accessing union members.
Only one union member should be accessed at a time. Because a union stores its members on top of each other, it's important to access only one member at a time.
The union Keyword
union tag {
union_member(s);
/* additional statements may go here */
}instance;
The union keyword is used for declaring unions. A union is a collection of one or more variables (union_members) that have been grouped under a single name. In addition, each of these union members occupies the same area of memory.
The keyword union identifies the beginning of a union definition. It's followed by a tag that is the name given to the union. Following the tag are the union members enclosed in braces.
An instance, the actual declaration of a union, also can be defined. If you define the structure without the instance, it's just a template that can be used later in a program to declare structures. The following is a template's format:
union tag {
union_member(s);
/* additional statements may go here */
};
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);
setzechar('\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, bei dem die zulässigen Werte zwischen 10 und 110 liegen.
Ä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.