Kapitel – 5
Einführung in die C-Programmierung
Einführung
„C“ ist eine der beliebtesten Computersprachen in der heutigen Computerwelt. Die Programmiersprache „C“ wurde 1972 von Brian Kernighan und Dennis Ritchie in den Bell Research Labs entworfen und entwickelt.
'C' ist eine Sprache, die speziell geschaffen wurde, um dem Programmierer den Zugriff auf fast alle Interna der Maschine zu ermöglichen - Register, I/O-Slots und absolute Adressen. Gleichzeitig ermöglicht 'C' so viel Datenhandhabung und Modularisierung von programmiertem Text wie nötig, um zu ermöglichen, dass sehr komplexe Multi-Programmierer-Projekte in einer organisierten und zeitgerechten Weise erstellt werden.
Obwohl diese Sprache ursprünglich für die Ausführung unter UNIX vorgesehen war, bestand großes Interesse daran, sie unter dem MS-DOS-Betriebssystem auf dem IBM-PC und kompatiblen Geräten auszuführen. Es ist aufgrund der Einfachheit des Ausdrucks, der Kompaktheit des Codes und des breiten Anwendungsbereichs eine hervorragende Sprache für diese Umgebung.
Aufgrund der Einfachheit und Einfachheit, einen C-Compiler zu schreiben, ist es normalerweise die erste Hochsprache, die auf jedem neuen Computer verfügbar ist, einschließlich Mikrocomputern, Minicomputern und Großrechnern.
Warum C in der Datenwiederherstellungsprogrammierung verwenden?
In der heutigen Welt der Computerprogrammierung sind viele Hochsprachen verfügbar. Diese Sprachen sind gut mit vielen Funktionen, die für die meisten Programmieraufgaben geeignet sind. Es gibt jedoch mehrere Gründe, warum C die erste Wahl der Programmierer ist, die bereit sind, für die Datenwiederherstellung, Systemprogrammierung, Geräteprogrammierung oder Hardwareprogrammierung zu programmieren:
- C ist eine beliebte Sprache, die von professionellen Programmierern bevorzugt wird. Dadurch steht eine Vielzahl von C-Compilern und hilfreichem Zubehör zur Verfügung.
- C ist eine portable Sprache. Ein C-Programm, das für ein Computersystem geschrieben wurde, kann kompiliert und auf einem anderen System mit wenig oder keiner Modifikation ausgeführt werden. Die Portabilität wird durch den ANSI-Standard für C, das Regelwerk für C-Compiler, verbessert.
- C erlaubt eine breite Verwendung von Modulen in der Programmierung. C-Code kann in Routinen geschrieben werden, die als Funktionen bezeichnet werden. Diese Funktionen können in anderen Anwendungen oder Programmen wiederverwendet werden. Sie müssen keine zusätzlichen Anstrengungen bei der Programmierung einer neuen Anwendung unternehmen, um dasselbe Modul zu erstellen, das Sie zuvor in einer anderen Anwendungsprogrammierung entwickelt haben.
Sie können diese Funktion in einem neuen Programm ohne Änderungen oder geringfügige Änderungen verwenden. Im Fall der Datenrettungsprogrammierung werden Sie diese Qualität sehr hilfreich finden, wenn Sie die gleichen 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 eine Handvoll Begriffe, sogenannte Schlüsselwörter, enthält, die als Basis dienen, auf der die Funktionalität der Sprache aufbaut. Diese Schlüsselwörter, auch reservierte Wörter genannt, machen es leistungsfähiger und geben den weiten Bereich der Programmierung und geben einem Programmierer das Gefühl, jede Art von Programmierung in C zu machen.
Lassen Sie mich annehmen, dass Sie nichts in C wissen
Ich gehe davon aus, dass Sie nichts über C-Programmierung wissen und auch keine Ahnung vom Programmieren haben. Ich werde mit den grundlegendsten Konzepten von C beginnen und Sie auf die höhere Ebene der C-Programmierung führen, einschließlich der normalerweise einschüchternden Konzepte von Zeigern, Strukturen und dynamischer Zuordnung.
Um diese Konzepte vollständig zu verstehen, wird es einige Zeit und Arbeit Ihrerseits erfordern, da sie nicht besonders leicht zu verstehen sind, aber sehr mächtige Werkzeuge sind.
Das Programmieren in C ist ein enormer Vorteil in den Bereichen, in denen Sie möglicherweise die Assemblersprache verwenden müssen, es aber lieber als einfach zu schreibendes und leicht zu wartendes Programm halten möchten. Die Zeitersparnis beim Programmieren von C kann in solchen Fällen enorm sein.
Obwohl die Sprache C einen guten Ruf genießt, wenn Programme von einer Implementierung zu einer anderen transportiert werden, gibt es Unterschiede bei den Compilern, die Sie jedes Mal feststellen werden, wenn Sie versuchen, einen anderen Compiler zu verwenden.
Die meisten Unterschiede werden offensichtlich, wenn Sie nicht standardmäßige Erweiterungen wie Aufrufe des DOS-BIOS verwenden, wenn Sie MS-DOS verwenden, aber selbst diese Unterschiede können durch sorgfältige Auswahl von Programmierkonstrukten minimiert werden.
Als sich herausstellte, dass die Programmiersprache C zu einer sehr beliebten Sprache wurde, die auf einer Vielzahl von Computern verfügbar war, traf sich eine Gruppe besorgter Personen, um einen Standardsatz von Regeln für die Verwendung der Programmiersprache C vorzuschlagen.
Die Gruppe vertrat alle Sektoren der Softwareindustrie und nach vielen Treffen und vielen Vorentwürfen schrieben sie schließlich einen akzeptablen Standard für die Sprache C. Er wurde vom American National Standards Institute (ANSI) und vom Internationale Organisation für Normung (ISO).
Es wird keiner Gruppe oder keinem Benutzer aufgezwungen, aber da es so weit verbreitet ist, wäre es für jeden Compiler-Schreiber wirtschaftlichen Selbstmord, sich dem Standard zu widersetzen.
Die in diesem Buch geschriebenen Programme sind hauptsächlich für die Verwendung auf einem IBM-PC oder einem kompatiblen Computer gedacht, können aber mit jedem ANSI-Standard-Compiler verwendet werden, da er so eng mit dem ANSI-Standard konform ist.
Lass uns anfangen
Bevor Sie irgendetwas in irgendeiner Sprache tun und mit dem Programmieren beginnen können, müssen Sie wissen, wie man einen Bezeichner benennt. 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 ein Buchstabe des Alphabets oder ein Unterstrich und der Rest ein beliebiger Buchstabe des Alphabets ist Alphabet, eine beliebige Ziffer oder der Unterstrich.
Bei der Benennung von Bezeichnern sind zwei Regeln zu beachten.
- Der Fall von alphabetischen Zeichen ist signifikant. C ist eine Sprache, die zwischen Groß- und Kleinschreibung unterscheidet. Das heißt, Recovery unterscheidet sich von Recovery und RECOVERY unterscheidet sich von den 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 angesehen. Wenn mehr als 31 verwendet werden, können alle Zeichen nach dem 31. von einem bestimmten Compiler ignoriert werden.
Schlüsselwörter
In C sind 32 Wörter als Schlüsselwörter definiert. Diese haben vordefinierte Verwendungen und können in einem C-Programm nicht für andere Zwecke verwendet werden. Sie werden vom Compiler als Hilfe beim Kompilieren des Programms verwendet. Sie werden immer klein geschrieben. Eine vollständige Liste folgt:
auto |
break |
case |
char |
const |
continue |
default |
do |
double |
else |
enum |
extern |
float |
for |
goto |
if |
int |
long |
register |
return |
short |
signed |
sizeof |
static |
struct |
switch |
typedef |
union |
unsigned |
void |
volatile |
while |
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 hat zwei zu berücksichtigende Einheiten, 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:
/* First Program to learn C */
#include <stdio.h>
void main()
{
printf("This is a C program\n"); // printing a message
}
Obwohl das Programm sehr einfach ist, sind einige Punkte bemerkenswert. Betrachten wir das obige Programm. Alles, was innerhalb von /* 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:
/* das ist ein /* Kommentar */ innerhalb eines Kommentars, was falsch ist */
Es gibt auch eine Möglichkeit der Dokumentation, die innerhalb einer Zeile funktioniert. Durch die Verwendung von // können wir dieser Zeile eine kleine 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, deshalb haben wir void main geschrieben. Wir könnten dieses Programm auch schreiben als:
/* Erstes Programm zum Erlernen von C*/
#include <stdio.h>
main()
{
printf("This is a C program\n"); // printing a message
return 0;
}
Beide Programme sind gleich und führen die gleiche Aufgabe aus. Das Ergebnis beider Programme druckt die folgende Ausgabe auf dem Bildschirm:
Dies ist ein C-Programm
#include<stdio.h> ermöglicht dem Programm, mit dem Bildschirm, der Tastatur und dem Dateisystem Ihres Computers zu interagieren. Sie finden es am Anfang fast jedes C-Programms.
main() deklariert den Beginn der Funktion, während die beiden geschweiften Klammern den Beginn und das Ende der Funktion anzeigen. Geschweifte Klammern werden in C verwendet, um Anweisungen wie in einer Funktion oder im Rumpf einer Schleife zu gruppieren. Eine solche Gruppierung wird als zusammengesetzte Anweisung oder Block bezeichnet.
printf("Dies ist ein C-Programm\n"); gibt die Wörter auf dem Bildschirm aus. Der zu druckende Text wird in doppelte Anführungszeichen eingeschlossen. Das \n am Ende des Textes weist das Programm an, eine neue Zeile als Teil der Ausgabe auszugeben. Die Funktion printf() wird für die Monitoranzeige der Ausgabe verwendet.
Die meisten C-Programme sind in Kleinbuchstaben geschrieben. Sie finden normalerweise Großbuchstaben, die in Präprozessordefinitionen verwendet werden, die später besprochen werden, oder innerhalb von Anführungszeichen als Teile von Zeichenketten.
Kompilieren des Programms
Der Name unseres Programms sei CPROG.C. Um das C-Programm einzugeben und zu kompilieren, gehen Sie folgendermaßen vor:
- Machen Sie das aktive Verzeichnis Ihrer C-Programme und starten Sie Ihren Editor. Dazu kann jeder Texteditor verwendet werden, aber die meisten C-Compiler wie Turbo C++ von Borland verfügen über eine integrierte Entwicklungsumgebung (IDE), mit der Sie Ihre Programme in einer bequemen 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, die besagt, 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 bei der Eingabe des Programms ein Fehler auftritt, fängt der Compiler ihn ab und zeigt eine Fehlermeldung an. Korrigieren Sie den Fehler, der in der Fehlermeldung angezeigt wird.
- Ihr erstes C-Programm sollte jetzt kompiliert und betriebsbereit sein. Wenn Sie eine Verzeichnisliste aller Dateien mit dem Namen CPROG anzeigen, erhalten Sie die vier Dateien mit unterschiedlicher Erweiterung, 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 Linken von CPROG.C erstellt wurde
- Um CPROG.EXE auszuführen oder auszuführen, geben Sie einfach cprog ein. Die Meldung Dies ist ein C-Programm wird auf dem Bildschirm angezeigt.
Betrachten wir nun das folgende Programm:
/* Erstes Programm zum Erlernen von C */ // 1
// 2
#include <stdio.h> // 3
// 4
main() // 5
{
// 6
printf("This is a C program\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 zerlegen. 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 wird eine Beschreibung des Fehlers.
Diese Meldung ist ziemlich informativ und teilt Ihnen mit, dass der Compiler in Zeile 8 von CPROG.C erwartet hat, ein Semikolon zu finden, es aber nicht getan hat. Sie wissen jedoch, dass das Semikolon in Zeile 7 weggelassen wurde, also gibt es eine Diskrepanz.
Warum der Compiler in Zeile 8 einen Fehler meldet, obwohl in Zeile 7 tatsächlich ein Semikolon weggelassen wurde. Die Antwort liegt darin, dass C sich nicht um Dinge wie Zeilenumbrüche kümmert. 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 nach dem 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 kann eine Reihe von Möglichkeiten für verschiedene Arten von Fehlern geben. Lassen Sie uns das Verknüpfen von Fehlermeldungen besprechen. Linker-Fehler sind relativ selten und resultieren normalerweise aus einer falschen Schreibweise des Namens einer C-Bibliotheksfunktion. In diesem Fall erhalten Sie die Fehlermeldung „Error: Undefined Symbols:“, gefolgt vom falsch geschriebenen Namen. Sobald Sie die Rechtschreibung 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(“ The Number Is %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 Arten von Variablen zu signalisieren. Das Zeichen nach dem %-Zeichen ist ein d, das der Ausgaberoutine signalisiert, einen Dezimalwert zu erhalten 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, aber die meisten befinden sich am Anfang jeder Funktion. Die meisten lokalen Variablen werden erstellt, wenn die Funktion aufgerufen wird, und werden bei der Rückkehr von dieser Funktion zerstört.
Um Variablen in Ihren C-Programmen zu verwenden, müssen Sie die folgenden Regeln kennen, wenn Sie Variablen in C Namen geben:
- Der Name darf Buchstaben, Ziffern und den Unterstrich (_) enthalten.
- Das erste Zeichen des Namens muss ein Buchstabe sein. Der Unterstrich ist auch ein legales erstes Zeichen, aber seine Verwendung wird nicht empfohlen.
- C unterscheidet zwischen Groß- und Kleinschreibung, daher unterscheidet sich der Variablenname num von Num.
- C-Schlüsselwörter können nicht als Variablennamen verwendet werden. Ein Schlüsselwort ist ein Wort, das Teil der C-Sprache ist.
Die folgende Liste enthält einige Beispiele für legale und illegale C-Variablennamen:
Variable Name |
Legal or Not |
Num |
Legal |
Ttpt2_t2p |
Legal |
Tt pt |
Illegal: Space is not allowed |
_1990_tax |
Legal but not advised |
Jack_phone# |
Illegal: Contains the illegal character # |
Case |
Illegal: Is a C keyword |
1book |
Illegal: First character is a digit |
Die erste neue Sache, die 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 folgendermaßen geschrieben werden können:
int num; /* define uninitialized variable 'num' */
/* and after all variable definitions: */
num = 10; /* assigns value 10 to variable 'num' */
Variablen können am Anfang eines Blocks definiert werden (zwischen den geschweiften Klammern {und}), normalerweise steht dies am Anfang eines Funktionskörpers, aber es kann auch am Anfang eines anderen Blocktyps stehen.
Variablen, die am Anfang eines Blocks definiert werden, nehmen standardmäßig den Status „auto“ an. Das bedeutet, dass sie nur während der Ausführung des Bausteins existieren. Wenn die Ausführung der Funktion 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 folgendermaßen 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 überflüssig.
Manchmal ist dies jedoch nicht das, was Sie möchten. Angenommen, Sie möchten, dass eine Funktion zählt, wie oft sie aufgerufen wird. Wenn die Variable jedes Mal zerstört würde, wenn die Funktion zurückkehrt, wäre dies nicht möglich.
Daher ist es möglich, der Variablen eine sogenannte statische Dauer zu geben, was bedeutet, dass sie während der gesamten Ausführung des Programms intakt bleibt. Zum Beispiel:
static int num = 10;
Dies initialisiert die Variable num zu Beginn der Programmausführung auf 10. Von da an bleibt der Wert unangetastet; 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 bequem, den Wert über einen Parameter an alle anderen Funktionen zu übergeben, die ihn benötigen.
Wenn Sie jedoch Zugriff auf die Variable von allen Funktionen in der gesamten Quelldatei benötigen, können Sie dies auch mit dem Schlüsselwort static tun, aber indem Sie die Definition außerhalb aller Funktionen platzieren. Zum Beispiel:
#include <stdio.h>
static int num = 10; /* will be accessible from entire source file */
int main(void)
{
printf("The Number Is: %d\n", num);
return 0;
}
Und es gibt auch Fälle, in denen eine Variable vom gesamten Programm, das aus mehreren Quelldateien bestehen kann, zugänglich sein muss. Dies wird als globale Variable bezeichnet und sollte vermieden werden, wenn es nicht erforderlich ist.
Dies geschieht auch, indem die Definition außerhalb aller Funktionen platziert wird, jedoch ohne das statische Schlüsselwort:
#include <stdio.h>
int num = 10; /* will be accessible from entire program! */
int main(void)
{
printf("The Number Is: %d\n", num);
return 0;
}
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 von ihnen ist const. Eine als const definierte Variable darf nicht geändert werden.
Es gibt zwei weitere Modifikatoren, die weniger häufig verwendet werden. Der flüchtige und Registermodifikator. Der Modifizierer volatile erfordert, dass der Compiler bei jedem Lesen tatsächlich auf die Variable zugreift. Es kann die Variable nicht optimieren, indem es sie in ein Register oder so einfügt. Dies wird hauptsächlich für Multithreading- und Interrupt-Verarbeitungszwecke usw. verwendet.
Der Register-Modifizierer fordert den Compiler auf, die Variable in ein Register zu optimieren. Dies ist nur mit Auto-Variablen möglich, und in vielen Fällen kann der Compiler die Variablen besser auswählen, um sie in Register zu optimieren, sodass dieses Schlüsselwort veraltet ist. Die einzige direkte Folge der Erstellung eines variablen Registers ist, dass seine Adresse nicht übernommen werden kann.
Die Variablentabelle auf der nächsten Seite beschreibt die Speicherklasse von fünf Arten von Speicherklassen.
In der Tabelle sehen wir, dass das Schlüsselwort extern in zwei Zeilen platziert ist. Das Schlüsselwort extern wird in Funktionen verwendet, um eine statische externe Variable zu deklarieren, die woanders definiert ist.
Numerische Variablentypen
C stellt mehrere unterschiedliche Arten von numerischen Variablen zur Verfügung, da unterschiedliche numerische Werte unterschiedliche Speicheranforderungen haben. Diese numerischen Typen unterscheiden sich in der Leichtigkeit, mit der bestimmte mathematische Operationen an ihnen ausgeführt werden können.
Kleine Ganzzahlen erfordern weniger Speicherplatz zum Speichern, und Ihr Computer kann mathematische Operationen mit solchen Zahlen sehr schnell ausführen. Große Ganzzahlen und Gleitkommawerte erfordern mehr Speicherplatz und mehr Zeit für mathematische Operationen. Durch die Verwendung geeigneter Variablentypen stellen Sie sicher, dass Ihr Programm so effizient wie möglich läuft.
Die numerischen Variablen von C fallen in die folgenden zwei Hauptkategorien:
- Integer-Variablen
- Fließkommavariablen
Innerhalb jeder dieser Kategorien gibt es zwei oder mehr spezifische Variablentypen. Die folgende Tabelle zeigt die Speichermenge in Byte, die erforderlich ist, um eine einzelne Variable jedes Typs aufzunehmen.
Der Typ char kann entweder signed char oder unsigned char äquivalent sein, aber es ist immer ein separater Typ von beiden.
In C gibt es keinen Unterschied zwischen dem Speichern von Zeichen oder ihren entsprechenden numerischen Werten in einer Variablen, daher ist auch keine Funktion erforderlich, um zwischen einem Zeichen und seinem numerischen Wert oder umgekehrt umzuwandeln. Wenn Sie bei den anderen Integer-Typen signed oder unsigned weglassen, wird der Standardwert signiert, also z. int und signed int sind äquivalent.
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 sehr groß sind, ist es oft eine gute Idee, den Typ int zu verwenden; es ist normalerweise die Größe, mit der der Prozessor am einfachsten umgehen kann und daher am schnellsten.
Bei mehreren Compilern sind Double und Long Double gleichbedeutend. Das zusammen mit der Tatsache, dass die meisten mathematischen Standardfunktionen mit dem Typ Double arbeiten, ist ein guter Grund, immer den Typ Double zu verwenden, wenn Sie mit Bruchzahlen arbeiten müssen.
Die folgende Tabelle soll die Variablentypen besser beschreiben:
Häufig verwendete Spezialtypen:
Variable Type |
Description |
size_t |
unsigned type used for storing the sizes of objects in bytes |
time_t |
used to store results of the time() function |
clock_t |
used to store results of the clock() function |
FILE |
used for accessing a stream (usually a file or device) |
ptrdiff_t |
signed type of the difference between 2 pointers |
div_t |
used to store results of the div() function |
ldiv_t |
used to store results of ldiv() function |
fpos_t |
used to hold file position information |
va_list |
used in variable argument handling |
wchar_t |
wide character type (used for extended character sets) |
sig_atomic_t |
used in signal handlers |
Jmp_buf |
used for non-local jumps |
Um diese Variablen besser zu verstehen, nehmen wir ein Beispiel:
/* Programm, um den Bereich und die Größe der C-Variablen */
in Bytes anzugeben
#include <stdio.h>
int main()
{
int a; /* simple integer type */
long int b; /* long integer type */
short int c; /* short integer type */
unsigned int d; /* unsigned integer type */
char e; /* character type */
float f; /* floating point type */
double g; /* double precision floating point */
a = 1023;
b = 2222;
c = 123;
d = 1234;
e = 'X';
f = 3.14159;
g = 3.1415926535898;
printf( "\nA char is %d bytes", sizeof( char ));
printf( "\nAn int is %d bytes", sizeof( int ));
printf( "\nA short is %d bytes", sizeof( short ));
printf( "\nA long is %d bytes", sizeof( long ));
printf( "\nAn unsigned char is %d bytes",
sizeof( unsigned char ));
printf( "\nAn unsigned int is %d bytes",
sizeof( unsigned int ));
printf( "\nAn unsigned short is %d bytes",
sizeof( unsigned short ));
printf( "\nAn unsigned long is %d bytes",
sizeof( unsigned long ));
printf( "\nA float is %d bytes", sizeof( float ));
printf( "\nA double is %d bytes\n", sizeof( double ));
printf("a = %d\n", a); /* decimal output */
printf("a = %o\n", a); /* octal output */
printf("a = %x\n", a); /* hexadecimal output */
printf("b = %ld\n", b); /* decimal long output */
printf("c = %d\n", c); /* decimal short output */
printf("d = %u\n", d); /* unsigned output */
printf("e = %c\n", e); /* character output */
printf("f = %f\n", f); /* floating output */
printf("g = %f\n", g); /* double float output */
printf("\n");
printf("a = %d\n", a); /* simple int output */
printf("a = %7d\n", a); /* use a field width of 7 */
printf("a = %-7d\n", a); /* left justify in
field of 7 */
c = 5;
d = 8;
printf("a = %*d\n", c, a); /* use a field width of 5*/
printf("a = %*d\n", d, a); /* use a field width of 8 */
printf("\n");
printf("f = %f\n", f); /* simple float output */
printf("f = %12f\n", f); /* use field width of 12 */
printf("f = %12.3f\n", f); /* use 3 decimal places */
printf("f = %12.5f\n", f); /* use 5 decimal places */
printf("f = %-12.5f\n", f); /* left justify in field */
return 0;
}
Das Ergebnis des Programms nach der Ausführung wird angezeigt als:
A char is 1 bytes
An int is 2 bytes
A short is 2 bytes
A long is 4 bytes
An unsigned char is 1 bytes
An unsigned int is 2 bytes
An unsigned short is 2 bytes
An unsigned long is 4 bytes
A float is 4 bytes
A double is 8 bytes
a = 1023
a = 1777
a = 3ff
b = 2222
c = 123
d = 1234
e = X
f = 3.141590
g = 3.141593
a = 1023
a = 1023
a = 1023
a = 1023
a = 1023
f = 3.141590
f = 3.141590
f = 3.142
f = 3.14159
f = 3.14159 |
Vor ihrer Verwendung muss eine Variable in einem C-Programm deklariert werden. Eine Variablendeklaration teilt dem Compiler den Namen und Typ einer Variablen mit und initialisiert die Variable optional mit einem bestimmten Wert.
Wenn Ihr Programm versucht, eine nicht deklarierte Variable zu verwenden, 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; /* three integer variables */
float percent, total; /* two float variables */
Das typedef-Schlüsselwort
Das Schlüsselwort typedef wird verwendet, um einen neuen Namen für einen vorhandenen Datentyp zu erstellen. Tatsächlich erstellt typedef ein Synonym. Zum Beispiel die Anweisung
typedef int integer;
hier sehen wir, dass typedef integer als Synonym für int erzeugt. Sie können dann integer verwenden, um Variablen vom Typ int zu definieren, wie in diesem Beispiel:
ganzzahlige Anzahl;
typedef erstellt also keinen neuen Datentyp, es erlaubt Ihnen nur, einen anderen Namen für einen vordefinierten Datentyp zu verwenden.
Numerische Variablen initialisieren
Wenn eine Variable deklariert wird, wird der Compiler angewiesen, Speicherplatz für die Variable bereitzustellen. Der in diesem Bereich gespeicherte Wert, der Wert der Variablen, ist jedoch nicht definiert. Es könnte null sein, oder es könnte irgendein zufälliger "Müll" Wert. Bevor Sie eine Variable verwenden, sollten Sie sie immer auf einen bekannten Wert initialisieren. Nehmen wir dieses Beispiel:
int count; /* Set aside storage space for count */
count = 0; /* Store 0 in count */
Diese Anweisung verwendet das Gleichheitszeichen (=), das der Zuweisungsoperator von C ist. Sie können eine Variable auch initialisieren, wenn sie deklariert ist. Stellen Sie dazu in der Deklarationsanweisung nach dem Variablennamen ein Gleichheitszeichen und den gewünschten Anfangswert:
int count = 0;
double rate = 0.01, complexity = 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 Bereichs:
int amount = 100000;
unsigned int length = -2500;
Der C-Compiler fängt solche Fehler nicht ab. Ihr Programm wird möglicherweise kompiliert und gelinkt, aber Sie erhalten möglicherweise unerwartete Ergebnisse, wenn das Programm ausgeführt wird.
Nehmen wir das folgende Beispiel, um die Gesamtzahl der Sektoren auf einer Festplatte zu berechnen:
// Modellprogramm zur Berechnung von Sektoren auf einer Festplatte //
#include<stdio.h>
#define SECTOR_PER_SIDE 63
#define SIDE_PER_CYLINDER 254
void main()
{
int cylinder=0;
clrscr();
printf("Enter The No. of Cylinders in the Disk \n\n\t");
scanf("%d",&cylinder); // Get the value from the user //
printf("\n\n\t Total Number of Sectors in the disk = %ld", (long)SECTOR_PER_SIDE*SIDE_PER_CYLINDER* cylinder);
getch();
}
Die Ausgabe des Programms ist wie folgt:
Enter The No. of Cylinders in the Disk
1024
Total Number of Sectors in the disk = 16386048
In diesem Beispiel sehen wir drei neue Dinge zu 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 SEKTOR_PER_SEITE definiert, um das Programm leicht verständlich zu machen. Dasselbe gilt für #define SIDE_PER_CYLINDER 254. scanf() wird verwendet, um die Eingabe vom Benutzer zu erhalten.
Hier nehmen wir die Anzahl der Zylinder als Eingabe des Benutzers. * wird verwendet, um zwei oder mehr Werte zu multiplizieren, wie im Beispiel gezeigt.
Die
getch()-Funktion ruft grundsätzlich eine einzelne Zeicheneingabe von der Tastatur ab. Durch Eingabe von getch(); Hier halten wir den Bildschirm an, 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 gehören zu den folgenden vier Kategorien:
- Der Zuweisungsoperator
- Mathematische Operatoren
- Vergleichsoperatoren
- Logische Operatoren
Aufgabenverwalter
Der Zuweisungsoperator ist das Gleichheitszeichen (=). Die Verwendung des Gleichheitszeichens in der Programmierung unterscheidet sich von seiner Verwendung in regulären mathematischen algebraischen Beziehungen. Wenn du schreibst
x = y;
In einem C-Programm bedeutet es nicht "x ist gleich y." Stattdessen bedeutet es "den Wert von y x zuordnen". In einer C-Zuweisungsanweisung kann die rechte Seite ein beliebiger Ausdruck sein und die linke Seite muss ein Variablenname sein. Das Formular 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 durch. 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 einzigen Operanden annehmen. C hat zwei unäre mathematische Operatoren.
Die Inkrement- und Dekrementoperatoren können nur mit Variablen verwendet werden, nicht mit Konstanten. Die durchgeführte Operation besteht darin, Eins zu dem Operanden zu addieren oder Eins von ihm zu subtrahieren. Mit anderen Worten, die Anweisungen ++x; Andy; sind die Äquivalente dieser Aussagen:
x = x + 1;
y = y - 1;
binäre mathematische Operatoren nehmen zwei Operanden. Die ersten vier binären Operatoren, die die üblichen mathematischen Operationen eines Taschenrechners enthalten (+, -, *, /), sind Ihnen vertraut. Der fünfte Operator Modulus gibt den Rest zurück, wenn der erste Operand durch den zweiten Operanden dividiert wird. Zum Beispiel ist 11 Modul 4 gleich 3 (11 wird durch 4 geteilt, zweimal und 3 bleibt übrig).
Vergleichsoperatoren
Die Vergleichsoperatoren von C werden zum Vergleichen von Ausdrücken verwendet. Ein Ausdruck, der einen Vergleichsoperator enthält, ergibt entweder wahr (1) oder falsch (0). C hat sechs Vergleichsoperatoren.
Logische Operatoren
Mit logischen Operatoren von C können Sie zwei oder mehr relationale Ausdrücke zu einem einzigen Ausdruck kombinieren, der entweder als wahr oder falsch ausgewertet wird. Logische Operatoren werden entweder als 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 auf folgende Weise geschrieben werden:
(x > 1) && (x < 5)
(x >= 2) && (x <= 4)
Operator |
Symbol |
Description |
Example |
Assignment operators |
equal |
= |
assign the value of y to x |
x = y |
Mathematical operators |
Increment |
++ |
Increments the operand by one |
++x, x++ |
Decrement |
-- |
Decrements the operand by one |
--x, x-- |
Addition |
+ |
Adds two operands |
x + y |
Subtraction |
- |
Subtracts the second operand from the first |
x - y |
Multiplication |
* |
Multiplies two operands |
x * y |
Division |
/ |
Divides the first operand by the second operand |
x / y |
Modulus |
% |
Gives the remainder when the first operand is divided by the second operand |
x % y |
Relational operators |
Equal |
= = |
Equality |
x = = y |
Greater than |
> |
Greater than |
x > y |
Less than |
< |
Less than |
x < y |
Greater than or equal to |
>= |
Greater than or equal to |
x >= y |
Less than or equal to |
<= |
Less than or equal to |
x <= y |
Not equal |
!= |
Not equal to |
x != y |
Logical operators |
AND |
&& |
True (1) only if both exp1 and exp2 are true; false (0) otherwise |
exp1 && exp2 |
OR |
|| |
True (1) if either exp1 or exp2 is true; false (0) only if both are false |
exp1 || exp2 |
NOT |
! |
False (0) if exp1 is true; true (1) if exp1 is false |
!exp1 |
Dinge, die man sich über logische Ausdrücke merken sollte
x * = y |
is same as |
x = x * y |
y - = z + 1 |
is same as |
y = y - z + 1 |
a / = b |
is same as |
a = a / b |
x + = y / 8 |
is same as |
x = x + y / 8 |
y % = 3 |
is same as |
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 Unterausdrücke durch ein Komma trennen. Das Ergebnis lautet wie folgt:
- Beide Ausdrücke werden ausgewertet, wobei der linke Ausdruck zuerst ausgewertet wird.
- Der gesamte Ausdruck ergibt den Wert des rechten Ausdrucks.
Zum Beispiel weist die folgende Anweisung x den Wert von b zu, erhöht dann a und dann b:
x = (a++, b++);
Vorrang von C-Operatoren (Zusammenfassung der C-Operatoren)
Rank and Associativity |
Operators |
1(left to right) |
() [] -> . |
2(right to left) |
! ~ ++ -- * (indirection) & (address-of) (type)
sizeof + (unary) - (unary) |
3(left to right) |
* (multiplication) / % |
4(left to right) |
+ - |
5(left to right) |
<< >> |
6(left to right) |
< <= > >= |
7(left to right) |
= = != |
8(left to right) |
& (bitwise AND) |
9(left to right) |
^ |
10(left to right) |
| |
11(left to right) |
&& |
12(left to right) |
|| |
13(right to left) |
?: |
14(right to left) |
= += -= *= /= %= &= ^= |= <<= >>= |
15(left to right) |
, |
() is the function operator; [] is the array operator. |
|
Nehmen wir ein Beispiel für die Verwendung von Operatoren:
/* Verwendung von Operatoren */
int main()
{
int x = 0, y = 2, z = 1025;
float a = 0.0, b = 3.14159, c = -37.234;
/* incrementing */
x = x + 1; /* This increments x */
x++; /* This increments x */
++x; /* This increments x */
z = y++; /* z = 2, y = 3 */
z = ++y; /* z = 4, y = 4 */
/* decrementing */
y = y - 1; /* This decrements y */
y--; /* This decrements y */
--y; /* This decrements y */
y = 3;
z = y--; /* z = 3, y = 2 */
z = --y; /* z = 1, y = 1 */
/* arithmetic op */
a = a + 12; /* This adds 12 to a */
a += 12; /* This adds 12 more to a */
a *= 3.2; /* This multiplies a by 3.2 */
a -= b; /* This subtracts b from a */
a /= 10.0; /* This divides a by 10.0 */
/* conditional expression */
a = (b >= 3.0 ? 2.0 : 10.5 ); /* This expression */
if (b >= 3.0) /* And this expression */
a = 2.0; /* are identical, both */
else /* will cause the same */
a = 10.5; /* result. */
c = (a > b ? a : b); /* c will have the max of a or b */
c = (a > b ? b : a); /* c will have the min of a or b */
printf("x=%d, y=%d, z= %d\n", x, y, z);
printf("a=%f, b=%f, c= %f", a, b, c);
return 0;
}
und das Ergebnis dieses Programms wird auf dem Bildschirm angezeigt als:
x=3, y=1, z=1
a=2.000000, b=3.141590, c=2.000000
Etwas mehr über printf() und Scanf()
Betrachten Sie die folgenden beiden printf-Anweisungen
printf(“\t %d\n”, num);
printf(“%5.2f”, frakt);
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 dezimale Ganzzahl ausgegeben werden soll. \n bewirkt, dass die neue Ausgabe ab einer neuen Zeile beginnt.
In der zweiten printf-Anweisung teilt %5.2f dem Compiler mit, dass die Ausgabe in Fließkommazahlen erfolgen muss, mit insgesamt fünf Stellen und zwei Stellen rechts vom Dezimalkomma. Weitere Informationen zum Backslash-Zeichen finden Sie in der folgenden Tabelle:
Constant |
Meaning |
‘\a’ |
Audible alert (bell) |
‘\b’ |
Backspace |
‘\f’ |
Form feed |
‘\n’ |
New line |
‘\r’ |
Carriage return |
‘\t’ |
Horizontal tab |
‘\v’ |
Vertical tab |
‘\’’ |
Single quote |
‘\”’ |
Double quote |
‘\?’ |
Question mark |
‘\\’ |
Backslash |
‘\0’ |
Null |
Betrachten wir die folgende scanf-Anweisung
scanf(“%d”, &num);
Die Daten von der Tastatur werden von der scanf-Funktion empfangen. Im obigen Format wird das & Das Symbol (kaufmännisches Und) vor jedem Variablennamen ist ein Operator, der die Adresse des Variablennamens angibt.
Dadurch stoppt die Ausführung und wartet darauf, dass der Wert der Variablen num eingegeben wird. Wenn der ganzzahlige 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 aufgelistet:
Code |
Reads... |
%c |
Single character |
%d |
Decimal integer |
%e |
Floating point value |
%f |
Floating point value |
%g |
Floating point value |
%h |
Short integer |
%i |
Decimal, hexadecimal or octal integer |
%o |
Octal integer |
%s |
String |
%u |
Unsigned decimal integer |
%x |
Hexadecimal integer |
Kontrollanweisungen
Ein Programm besteht aus einer Reihe von Anweisungen, die normalerweise nacheinander ausgeführt werden. Programme können viel mächtiger sein, wenn wir die Reihenfolge kontrollieren können, in der Anweisungen ausgeführt werden.
Anweisungen fallen in drei allgemeine Typen:
- Zuweisung, bei der Werte, normalerweise die Ergebnisse von Berechnungen, in Variablen gespeichert werden.
- Eingabe / Ausgabe, Daten werden eingelesen oder ausgedruckt.
- Kontrolle, das Programm entscheidet, was als nächstes zu tun ist.
Dieser Abschnitt behandelt die Verwendung von Steueranweisungen in C. Wir zeigen, wie sie verwendet werden können, um leistungsstarke Programme zu schreiben, indem;
- Wiederholung wichtiger Programmabschnitte.
- Auswählen zwischen optionalen Abschnitten eines Programms.
Die if else-Anweisung
Dies wird verwendet, um zu entscheiden, ob an einem bestimmten Punkt etwas getan werden soll, oder um sich zwischen zwei Vorgehensweisen zu entscheiden.
Der folgende Test entscheidet, ob ein Student eine Prüfung mit der Mindestpunktzahl 45 bestanden hat
if (result >= 45)
printf("Pass\n");
else
printf("Fail\n");
It is possible to use the if part without the else.
if (temperature < 0)
print("Frozen\n");
Jede Version besteht aus einem Test, in der geklammerten Anweisung nach dem if. Wenn der Test wahr ist, wird die nächste Aussage befolgt. Wenn es falsch ist, wird die Anweisung nach dem else befolgt, falls vorhanden. Danach läuft der Rest des Programms normal weiter.
Wenn wir mehr als eine Anweisung nach if oder else haben möchten, sollten sie zwischen geschweiften Klammern gruppiert werden. Eine solche Gruppierung wird zusammengesetzte Anweisung oder Block genannt.
if (result >= 45)
{ printf("Passed\n");
printf("Congratulations\n");
}
else
{ printf("Failed\n");
printf("Better Luck Next Time\n");
}
Jede Version besteht aus einem Test, in der geklammerten Anweisung nach dem if. Wenn der Test wahr ist, wird die nächste Aussage befolgt. Wenn es falsch ist, wird die Anweisung nach dem else befolgt, falls vorhanden. Danach läuft der Rest des Programms normal weiter.
Wenn wir mehr als eine Anweisung nach if oder else haben möchten, sollten sie zwischen geschweiften Klammern gruppiert werden. Eine solche Gruppierung wird zusammengesetzte Anweisung oder Block genannt.
if (result <=100 && result >= 75)
printf("Passed: Grade A\n");
else if (result >= 60)
printf("Passed: Grade B\n");
else if (result >= 45)
printf("Passed: Grade C\n");
else
printf("Failed\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 umfassen. Dasselbe Muster kann mit mehr oder weniger ifs verwendet werden, und das abschließende else allein kann weggelassen werden.
Es ist Sache des Programmierers, 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;
for(num = 0 ; num < 10 ; num = num + 1)
{
if (num == 2)
printf("num is now equal to %d\n", num);
if (num < 5)
printf("num is now %d, which is less than 5\n", num);
else
printf("num is now %d, which is greater than 4\n", num);
} /* end of for loop */
return 0;
}
Ergebnis des Programms
num is now 0, which is less than 5
num is now 1, which is less than 5
num is now equal to 2
num is now 2, which is less than 5
num is now 3, which is less than 5
num is now 4, which is less than 5
num is now 5, which is greater than 4
num is now 6, which is greater than 4
num is now 7, which is greater than 4
num is now 8, which is greater than 4
num is now 9, which is greater than 4
Die Switch-Anweisung
Dies ist eine andere Form der Mehrwegentscheidung. Es ist gut strukturiert, kann aber nur in bestimmten Fällen verwendet werden, in denen;
- Nur eine Variable wird getestet, alle Zweige müssen vom Wert dieser Variablen 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. Optional kann ein abschließender Standardzweig verwendet werden, um alle nicht spezifizierten Fälle abzufangen.
Das unten angegebene Beispiel wird die Dinge verdeutlichen. Dies ist eine Funktion, die eine ganze Zahl in eine vage Beschreibung umwandelt. Es ist nützlich, wenn wir nur daran interessiert sind, eine Menge zu messen, wenn sie ziemlich klein ist.
estimate(number)
int number;
/* Estimate a number as none, one, two, several, many */
{ switch(number) {
case 0 :
printf("None\n");
break;
case 1 :
printf("One\n");
break;
case 2 :
printf("Two\n");
break;
case 3 :
case 4 :
case 5 :
printf("Several\n");
break;
default :
printf("Many\n");
break;
}
}
Jeder interessante Fall wird mit einer entsprechenden Aktion aufgelistet. Die break-Anweisung verhindert, dass weitere Anweisungen ausgeführt werden, indem der Schalter verlassen wird. Da Fall 3 und Fall 4 keine nachfolgende Unterbrechung haben, erlauben sie weiterhin die gleiche Aktion für mehrere Zahlenwerte.
Sowohl if- als auch switch-Konstrukte ermöglichen es 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;
for (num = 3 ; num < 13 ; num = num + 1)
{
switch (num)
{
case 3 :
printf("The value is three\n");
break;
case 4 :
printf("The value is four\n");
break;
case 5 :
case 6 :
case 7 :
case 8 :
printf("The value is between 5 and 8\n");
break;
case 11 :
printf("The value is eleven\n");
break;
default :
printf("It is one of the undefined values\n");
break;
} /* end of switch */
} /* end of for loop */
return 0;
}
Die Ausgabe des Programms ist
The value is three
The value is four
The value is between 5 and 8
The value is between 5 and 8
The value is between 5 and 8
The value is between 5 and 8
It is one of the undefined values
It is one of the undefined values
The value is eleven
It is one of the undefined values
Die Break-Anweisung
Wir sind break bereits in der Diskussion der switch-Anweisung begegnet. Es wird verwendet, um eine Schleife oder einen Schalter zu verlassen, wobei die Steuerung an die erste Anweisung hinter der Schleife oder einem Schalter übergeben wird.
Bei Schleifen kann break verwendet werden, um ein frühes Verlassen der Schleife zu erzwingen oder um 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 Kontrolle der Exit-Bedingung bereitstellt.
Die Continue-Anweisung
Dies ist ähnlich wie Break, tritt aber seltener auf. Es funktioniert nur innerhalb von Schleifen, wo seine Wirkung darin besteht, einen sofortigen Sprung zur Schleifensteueranweisung zu erzwingen.
- Springe in einer While-Schleife zur Testanweisung.
- In einer Do-While-Schleife zur Test-Anweisung springen.
- Springe in einer for-Schleife zum Test und führe die Iteration durch.
Wie ein break sollte Continue durch eine if-Anweisung geschützt werden. Es ist unwahrscheinlich, dass Sie es sehr oft verwenden. Um die Verwendung von break and Continue besser zu verstehen, sehen wir uns das folgende Programm an:
#include <stdio.h>
int main()
{
int value;
for(value = 5 ; value < 15 ; value = value + 1)
{
if (value == 8)
break;
printf("In the break loop, value is now %d\n", value);
}
for(value = 5 ; value < 15 ; value = value + 1)
{
if (value == 8)
continue;
printf("In the continue loop, value is now %d\n", value);
}
return 0;
}
Die Ausgabe des Programms sieht wie folgt aus:
In the break loop, value is now 5
In the break loop, value is now 6
In the break loop, value is now 7
In the continue loop, value is now 5
In the continue loop, value is now 6
In the continue loop, value is now 7
In the continue loop, value is now 9
In the continue loop, value is now 10
In the continue loop, value is now 11
In the continue loop, value is now 12
In the continue loop, value is now 13
In the continue loop, value is now 14
Schleifen
Der andere Haupttyp der Steueranweisung 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 der Weg von C, dies zu erreichen.
C gibt Ihnen die Wahl zwischen drei Arten von Schleifen, while, do-while und for.
- Die While-Schleife wiederholt eine Aktion, bis ein zugehöriger Test falsch 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 findet statt, nachdem der Schleifenkörper ausgeführt wurde. Dadurch wird sichergestellt, dass der Schleifenkörper mindestens einmal durchlaufen wird.
- Die for-Schleife wird häufig verwendet, normalerweise wenn die Schleife eine festgelegte Anzahl von Malen durchlaufen wird. Es ist sehr flexibel, und unerfahrene Programmierer sollten darauf achten, die gebotene Leistung nicht zu missbrauchen.
Die While-Schleife
Die While-Schleife wiederholt eine Anweisung, bis sich der Test ganz oben als falsch erweist. Als Beispiel ist hier eine Funktion, um die Länge eines Strings zurückzugeben. Denken Sie daran, dass die Zeichenfolge als ein Array von Zeichen dargestellt wird, das durch ein Nullzeichen '\0' abgeschlossen wird.
int string_length(char string[])
{ int i = 0;
while (string[i] != '\0')
i++;
return(i);
}
Der String wird als Argument an die Funktion übergeben. Die Größe des Arrays ist nicht angegeben, die Funktion funktioniert für eine Zeichenfolge beliebiger Größe.
Die While-Schleife wird verwendet, um die Zeichen in der Zeichenfolge nacheinander zu betrachten, bis das Nullzeichen gefunden wird. Dann wird die Schleife verlassen und der Index der Null zurückgegeben.
Solange das Zeichen nicht null ist, wird der Index erhöht und der Test wiederholt. Wir werden später in die Tiefe von Arrays gehen. Sehen wir uns ein Beispiel für eine While-Schleife an:
#include <stdio.h>
int main()
{
int count;
count = 0;
while (count < 6)
{
printf("The value of count is %d\n", count);
count = count + 1;
}
return 0;
}
und das Ergebnis wird wie folgt angezeigt:
The value of count is 0
The value of count is 1
The value of count is 2
The value of count is 3
The value of count is 4
The value of count is 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.
Eine solche Anordnung wird häufig dort verwendet, wo Daten gelesen werden sollen. Der Test überprüft dann die Daten und springt zurück, um erneut zu lesen, wenn sie nicht akzeptabel waren.
do
{
printf("Enter 1 for yes, 0 for no :");
scanf("%d", &input_value);
} while (input_value != 1 && input_value != 0)
Um die Do While-Schleife besser zu verstehen, sehen wir uns das folgende Beispiel an:
#include <stdio.h>
int main()
{
int i;
i = 0;
do
{
printf("The value of i is now %d\n", i);
i = i + 1;
} while (i < 5);
return 0;
}
Das Ergebnis des Programms wird wie folgt angezeigt:
The value of i is now 0
The value of i is now 1
The value of i is now 2
The value of i is now 3
The value of i is now 4
Die for-Schleife
Die for-Schleife funktioniert gut, wenn die Anzahl der Iterationen der Schleife bekannt ist, bevor in die Schleife eingetreten wird. Der Schleifenkopf besteht aus drei durch Semikolons 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 dies falsch zurückgibt.
- Die dritte ist eine Anweisung, die jedes Mal ausgeführt wird, wenn der Schleifenkörper abgeschlossen ist. Dies ist normalerweise ein Inkrement des Schleifenzählers.
Das Beispiel ist eine Funktion, die den Durchschnitt der in einem Array gespeicherten Zahlen berechnet. Die Funktion nimmt das Array und die Anzahl der Elemente als Argumente.
float average(float array[], int count)
{
float total = 0.0;
int i;
for(i = 0; i < count; i++)
total += array[i];
return(total / count);
}
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 machen normalerweise jeweils nur eine Sache, jedoch kann jede von ihnen leer gelassen werden. Eine leere erste oder letzte Anweisung bedeutet keine Initialisierung oder laufendes Inkrement. Eine leere Vergleichsanweisung wird immer als wahr behandelt. Dadurch wird die Schleife auf unbestimmte Zeit ausgeführt, sofern sie nicht auf andere Weise unterbrochen wird. Dies kann eine return- oder eine break-Anweisung sein.
Es ist auch möglich, mehrere Anweisungen an die erste oder dritte Position zu quetschen und sie durch Kommas zu trennen. Dies ermöglicht eine Schleife mit mehr als einer steuernden Variablen. 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,
for(hi = 100, lo = 0; hi >= lo; hi--, lo++)
Die for-Schleife ist äußerst flexibel und ermöglicht es, viele Arten von Programmverhalten einfach und schnell zu spezifizieren. Sehen wir uns ein Beispiel für eine for-Schleife an
#include <stdio.h>
int main()
{
int index;
for(index = 0 ; index < 6 ; index = index + 1)
printf("The value of the index is %d\n", index);
return 0;
}
Das Ergebnis des Programms wird wie folgt angezeigt:
The value of the index is 0
The value of the index is 1
The value of the index is 2
The value of the index is 3
The value of the index is 4
The value of the index is 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 von dem symbolischen Namen, zu dem Sie springen möchten. Der Name wird dann an beliebiger Stelle im Programm platziert, gefolgt von einem Doppelpunkt. Sie können innerhalb einer Funktion fast überall springen, aber Sie dürfen nicht in eine Schleife springen, obwohl Sie aus einer Schleife springen dürfen.
Dieses spezielle Programm ist wirklich ein Chaos, aber es ist ein gutes Beispiel dafür, warum Softwareentwickler versuchen, die Verwendung der goto-Anweisung so weit wie möglich zu eliminieren. Die einzige Stelle in diesem Programm, an der es sinnvoll ist, goto zu verwenden, ist, wo das Programm in 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 zu springen, aber eine goto-Anweisung bringt Sie auf sehr präzise Weise aus allen drei heraus.
Einige Leute sagen, dass die goto-Anweisung unter keinen Umständen verwendet werden sollte, aber das ist engstirniges Denken. Wenn es eine Stelle gibt, an der ein goto eindeutig einen saubereren Kontrollfluss ausführt als ein anderes Konstrukt, können Sie es jedoch so verwenden, wie es im Rest des Programms auf Ihrem Monitor der Fall ist. Sehen wir uns das Beispiel an:
#include <stdio.h>
int main()
{
int dog, cat, pig;
goto real_start;
some_where:
printf("This is another line of the mess.\n");
goto stop_it;
/* the following section is the only section with a useable goto */
real_start:
for(dog = 1 ; dog < 6 ; dog = dog + 1)
{
for(cat = 1 ; cat < 6 ; cat = cat + 1)
{
for(pig = 1 ; pig < 4 ; pig = pig + 1)
{
printf("Dog = %d Cat = %d Pig = %d\n", dog, cat, pig);
if ((dog + cat + pig) > 8 ) goto enough;
}
}
}
enough: printf("Those are enough animals for now.\n");
/* this is the end of the section with a useable goto statement */
printf("\nThis is the first line of the code.\n");
goto there;
where:
printf("This is the third line of the code.\n");
goto some_where;
there:
printf("This is the second line of the code.\n");
goto where;
stop_it:
printf("This is the last line of this mess.\n");
return 0;
}
Sehen wir uns die angezeigten Ergebnisse an
Dog = 1 Cat = 1 Pig = 1
Dog = 1 Cat = 1 Pig = 2
Dog = 1 Cat = 1 Pig = 3
Dog = 1 Cat = 2 Pig = 1
Dog = 1 Cat = 2 Pig = 2
Dog = 1 Cat = 2 Pig = 3
Dog = 1 Cat = 3 Pig = 1
Dog = 1 Cat = 3 Pig = 2
Dog = 1 Cat = 3 Pig = 3
Dog = 1 Cat = 4 Pig = 1
Dog = 1 Cat = 4 Pig = 2
Dog = 1 Cat = 4 Pig = 3
Dog = 1 Cat = 5 Pig = 1
Dog = 1 Cat = 5 Pig = 2
Dog = 1 Cat = 5 Pig = 3
Those are enough animals for now.
This is the first line of the code.
This is the second line of the code.
This is the third line of the code.
This is another line of the mess.
This is the last line of this mess.
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. Bei der Deklaration eines Zeigers wird unmittelbar vor dem Zeigernamen ein Sternchen platziert .
Die Adresse des Speicherplatzes, an dem die Variable gespeichert ist, finden Sie heraus, indem Sie ein kaufmännisches Und vor den Variablennamen setzen.
int num; /* Normal integer variable */
int *numPtr; /* Pointer to an integer variable */
Das folgende Beispiel gibt den Variablenwert und die Adresse im Speicher dieser Variablen aus.
printf("Der Wert %d wird unter Adresse %X\n gespeichert", num, &num);
Um die Adresse der Variablen num dem Zeiger numPtr zuzuweisen, weisen Sie die Adresse der Variablen num zu, wie im folgenden Beispiel:
numPtr = #
Um herauszufinden, was an der Adresse gespeichert ist, auf die numPtr zeigt, muss die Variable dereferenziert werden. Die Dereferenzierung wird mit dem Sternchen erreicht, mit dem der Zeiger deklariert wurde.
printf("Der Wert %d wird unter Adresse %X\n gespeichert", *numPtr, numPtr);
Alle Variablen in einem Programm befinden sich im Speicher. Die unten angegebenen Anweisungen verlangen, dass der Compiler auf einem 32-Bit-Computer 4 Byte Speicher für die Fließkommavariable x reserviert und dann den Wert 6,5 hineinschreibt.
float x;
x = 6.5;
Da die Adressposition im Speicher einer beliebigen Variablen erhalten wird, indem der Operator & vor seinem Namen ist also &x die Adresse von x. C ermöglicht es uns, noch einen Schritt weiter zu gehen und eine Variable zu definieren, die als Zeiger bezeichnet wird und die Adresse anderer Variablen enthält. Vielmehr können wir sagen, dass der Zeiger auf eine andere Variable zeigt. Beispielsweise:
float x;
float* px;
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:
Untersuchen wir die folgenden Aussagen:
int var_x;
int* ptrX;
var_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("value of x : %d", var_x);
Die erste Zeile veranlasst den Compiler, einen Platz im Speicher für eine Ganzzahl zu reservieren. Die zweite Zeile weist den Compiler an, Platz zum Speichern eines Zeigers zu reservieren.
Ein Zeiger ist ein Speicherort für eine Adresse. Die dritte Zeile sollte Sie an die scanf-Anweisungen erinnern. Die Adresse "&" Der Operator weist den Compiler an, zu dem Ort zu gehen, an dem er var_x gespeichert hat, und dann die Adresse des Speicherorts an ptrX zu übergeben.
Das Sternchen * vor einer Variablen weist den Compiler an, den Zeiger zu dereferenzieren und in den Speicher zu gehen. Dann können Sie Zuweisungen zu Variablen vornehmen, die an diesem Ort gespeichert sind. Sie können auf eine Variable verweisen und über einen Zeiger auf ihre Daten zugreifen. Sehen wir uns ein Beispiel für Zeiger an:
/* illustration of pointer use */
#include <stdio.h>
int main()
{
int index, *pt1, *pt2;
index = 39; /* any numerical value */
pt1 = &index; /* the address of index */
pt2 = pt1;
printf("The value is %d %d %d\n", index, *pt1, *pt2);
*pt1 = 13; /* this changes the value of index */
printf("The value is %d %d %d\n", index, *pt1, *pt2);
return 0;
}
Die Ausgabe des Programms wird wie folgt angezeigt:
The value is 39 39 39
The value is 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], *there, one, two;
int *pt, list[100], index;
strcpy(strg, "This is a character string.");
/* the function strcpy() is to copy one string to another. we’ll read about strcpy() function in String Section later */
one = strg[0]; /* one and two are identical */
two = *strg;
printf("The first output is %c %c\n", one, two);
one = strg[8]; /* one and two are identical */
two = *(strg+8);
printf("The second output is %c %c\n", one, two);
there = strg+10; /* strg+10 is identical to &strg[10] */
printf("The third output is %c\n", strg[10]);
printf("The fourth output is %c\n", *there);
for (index = 0 ; index < 100 ; index++)
list[index] = index + 100;
pt = list + 27;
printf("The fifth output is %d\n", list[27]);
printf("The sixth output is %d\n", *pt);
return 0;
}
Die Ausgabe des Programms wird wie folgt aussehen:
The first output is T T
The second output is a a
The third output is c
The fourth output is c
The fifth output is 127
The sixth output is 127
Arrays
Ein Array ist eine Sammlung von Variablen des gleichen Typs. Einzelne Array-Elemente werden durch einen Integer-Index identifiziert. In C beginnt der Index bei Null und wird immer in eckige Klammern geschrieben.
Wir haben bereits eindimensionale Arrays kennengelernt, die so deklariert sind
int Ergebnisse[20];
Arrays können mehr Dimensionen haben, in diesem Fall könnten sie als
deklariert werden
int results_2d[20][5];
int results_3d[20][5][3];
Jeder Index hat seinen eigenen Satz eckiger Klammern. Ein Array wird in der Hauptfunktion deklariert und enthält normalerweise Details zu Dimensionen. Es ist möglich, anstelle eines Arrays einen anderen Typ namens Zeiger zu verwenden. Das bedeutet, dass die Abmessungen nicht sofort festgelegt sind, sondern der Platz nach Bedarf zugewiesen werden kann. Dies ist eine fortschrittliche Technik, die nur in bestimmten spezialisierten Programmen erforderlich ist.
Als Beispiel ist hier eine einfache Funktion, um alle Ganzzahlen in einem eindimensionalen Array zu addieren.
int add_array(int array[], int size)
{
int i;
int total = 0;
for(i = 0; i < size; i++)
total += array[i];
return(total);
}
Das als nächstes angegebene Programm erstellt einen String, greift auf einige Daten darin zu und druckt ihn aus. Greifen Sie erneut mit Zeigern darauf zu und geben Sie dann die Zeichenfolge aus. Es sollte "Hi!" und „012345678“ auf verschiedenen Leitungen. Sehen wir uns die Kodierung des Programms an:
#include <stdio.h>
#define STR_LENGTH 10
void main()
{
char Str[STR_LENGTH];
char* pStr;
int i;
Str[0] = 'H';
Str[1] = 'i';
Str[2] = '!';
Str[3] = '\0'; // special end string character NULL
printf("The string in Str is : %s\n", Str);
pStr = &Str[0];
for (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("The string in Str is : %s\n", Str);
}
[] (eckige Klammern) werden verwendet, um das Array zu deklarieren. Die Programmzeile char Str[STR_LENGTH]; deklariert ein Array mit zehn Zeichen. Das sind zehn individuelle Charaktere, die alle im Gedächtnis an der gleichen Stelle zusammengefügt werden. Auf sie kann über unseren Variablennamen Str zusammen mit einem [n] zugegriffen werden, wobei n die Elementnummer ist.
Wenn Sie über Arrays sprechen, sollten Sie immer daran denken, 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 also im Fall von Arrays immer von 0 bis zur Größe des Arrays - 1.
Beachten Sie als Nächstes, dass wir die Buchstaben "Hi!" in das Array, aber dann fügen wir ein '\0' ein Sie fragen sich wahrscheinlich, was das ist. "\0" steht für NULL und repräsentiert das Ende des Strings. Alle Zeichenfolgen müssen mit diesem Sonderzeichen '\0' enden. Wenn dies nicht der Fall ist und jemand printf für die Zeichenfolge aufruft, beginnt printf am Speicherort Ihrer Zeichenfolge und setzt den Druck fort, um zu sagen, dass es auf '\0' trifft, und Sie werden am Ende mit einem Haufen Müll enden Ihrer Saite. Achten Sie also darauf, Ihre Strings richtig zu terminieren.
Zeichen-Arrays
Eine String-Konstante wie z
"Ich bin ein String"
ist ein Array von Zeichen. Es wird intern in C durch die ASCII-Zeichen in der Zeichenfolge dargestellt, dh „I“, Leerzeichen, „a“, „m“, … oder die obige Zeichenfolge, und durch das spezielle Nullzeichen „\0“ abgeschlossen, damit Programme dies können Finden Sie das Ende der Zeichenfolge.
Zeichenfolgenkonstanten werden häufig verwendet, um die Ausgabe von Code mit printf verständlich zu machen:
printf("Hello, world\n");
printf("The value of a is: %f\n", a);
String-Konstanten können Variablen zugeordnet werden. C stellt die Zeichentypvariable bereit, die jeweils ein Zeichen (1 Byte) enthalten kann. Eine Zeichenfolge wird in einem Array von Zeichentypen gespeichert, ein ASCII-Zeichen pro Position.
Vergiss das nie, da Strings konventionell mit dem Nullzeichen „\0“ abgeschlossen werden, benötigen wir einen zusätzlichen Speicherplatz im Array.
C stellt keinen Operator zur Verfügung, 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()
{
char text_1[100], text_2[100], text_3[100];
char *ta, *tb;
int i;
/* set message to be an arrray */
/* of characters; initialize it */
/* to the constant string "..." */
/* let the compiler decide on */
/* its size by using [] */
char message[] = "Hello, I am a string; what are
you?";
printf("Original message: %s\n", message);
/* copy the message to text_1 */
i=0;
while ( (text_1[i] = message[i]) != '\0' )
i++;
printf("Text_1: %s\n", text_1);
/* use explicit pointer arithmetic */
ta=message;
tb=text_2;
while ( ( *tb++ = *ta++ ) != '\0' )
;
printf("Text_2: %s\n", text_2);
}
Die Ausgabe des Programms sieht wie folgt aus:
Original message: Hello, I am a string; what are you?
Text_1: Hello, I am a string; what are you?
Text_2: Hello, I am a string; what are you?
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 hinter dem Variablennamen in eckigen Klammern die Indexnummer. 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 gibt den Wert des dritten Elements in einem Array aus.
printf("%d\n", x[2]);
Das folgende Beispiel verwendet die scanf-Funktion, um einen Wert von der Tastatur in das letzte Element eines Arrays mit zehn Elementen einzulesen.
scanf("%d", &x[9]);
Array-Elemente initialisieren
Arrays können wie alle anderen Variablen durch 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 dreifachen Tabelle.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Das erspart das individuelle Zuweisen der Werte wie im folgenden Beispiel.
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 sequentiell indiziert wird, können wir die for-Schleife verwenden, um alle Werte eines Arrays anzuzeigen. Das folgende Beispiel zeigt alle Werte eines Arrays:
#include <stdio.h>
int main()
{
int x[10];
int counter;
/* Randomise the random number generator */
srand((unsigned)time(NULL));
/* Assign random values to the variable */
for (counter=0; counter<10; counter++)
x[counter] = rand();
/* Display the contents of the array */
for (counter=0; counter<10; counter++)
printf("element %d has the value %d\n", counter, x[counter]);
return 0;
}
Obwohl die Ausgabe jedes Mal die unterschiedlichen Werte ausgibt, wird das Ergebnis etwa so angezeigt:
element 0 has the value 17132
element 1 has the value 24904
element 2 has the value 13466
element 3 has the value 3147
element 4 has the value 22006
element 5 has the value 10397
element 6 has the value 28114
element 7 has the value 19817
element 8 has the value 27430
element 9 has the value 22136
Mehrdimensionale Arrays
Ein Array kann mehr als eine Dimension haben. Dadurch, dass das Array mehr als eine Dimension haben kann, wird eine größere Flexibilität bereitgestellt. Beispielsweise sind Tabellenkalkulationen auf einem zweidimensionalen Array aufgebaut; ein Array für die Zeilen und ein 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()
{
/* Declare a 2 x 5 multidimensional array */
int x[2][5] = { {1, 2, 3, 4, 5},
{2, 4, 6, 8, 10} };
int row, column;
/* Display the rows */
for (row=0; row<2; row++)
{
/* Display the columns */
for (column=0; column<5; column++)
printf("%d\t", x[row][column]);
putchar('\n');
}
return 0;
}
Die Ausgabe dieses Programms wird wie folgt angezeigt:
1 2 3 4 5
2 4 6 8 10
Saiten
Eine Zeichenfolge ist eine Gruppe von Zeichen, normalerweise Buchstaben des Alphabets, um Ihre Druckanzeige so zu formatieren, dass sie gut aussieht, aussagekräftige Namen und Titel hat und für Sie und die Benutzer ästhetisch ansprechend ist Ausgabe Ihres Programms.
Tatsächlich haben Sie in den Beispielen der vorherigen Themen bereits Zeichenfolgen verwendet. Aber es ist nicht die vollständige Einführung von Saiten. Es gibt viele mögliche Fälle in der Programmierung, in denen die Verwendung von formatierten Strings dem Programmierer hilft, zu viele Komplikationen im Programm und natürlich zu viele Fehler zu vermeiden.
Eine vollständige Definition einer Zeichenfolge ist eine Reihe von zeichenartigen Daten, die durch ein Nullzeichen ('\0') abgeschlossen werden.
Wenn C einen Datenstring auf irgendeine Weise verwenden will, um ihn entweder mit einem anderen String zu vergleichen, ihn auszugeben, ihn in einen anderen String zu kopieren oder was auch immer, werden die Funktionen so eingerichtet, dass sie das tun, wofür sie aufgerufen werden bis eine Null erkannt wird.
Es gibt keinen grundlegenden Datentyp für einen String in C Stattdessen; Strings werden in C als Array von Zeichen implementiert. Um beispielsweise einen Namen zu speichern, könnten 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]; /* Create a character array
called name */
printf("Enter your name: ");
gets(name);
printf("The name you entered was %s\n", name);
return 0;
}
Die Ausführung des Programms wird sein:
Enter your name: Tarun Tyagi
The name you entered was Tarun Tyagi
Einige allgemeine Zeichenfolgenfunktionen
Die Standardbibliothek string.h enthält viele nützliche Funktionen zum Bearbeiten von Zeichenfolgen. Einige der nützlichsten Funktionen wurden hier beispielhaft aufgeführt.
Die Strlen-Funktion
Die strlen-Funktion 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()
{
char name[80];
int length;
printf("Enter your name: ");
gets(name);
length = strlen(name);
printf("Your name has %d characters\n", length);
return 0;
}
Und die Ausführung des Programms wird wie folgt sein:
Enter your name: Tarun Subhash Tyagi
Your name has 19 characters
Enter your name: Preeti Tarun
Your name has 12 characters
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()
{
char first[80];
char second[80];
printf("Enter first string: ");
gets(first);
printf("Enter second string: ");
gets(second);
printf("first: %s, and second: %s Before strcpy()\n "
, first, second);
strcpy(second, first);
printf("first: %s, and second: %s After strcpy()\n",
first, second);
return 0;
}
und die Ausgabe des Programms sieht so aus:
Enter first string: Tarun
Enter second string: Tyagi
first: Tarun, and second: Tyagi Before strcpy()
first: Tarun, and second: Tarun After 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 mit dem Folgenden zu vergleichen, würden wir zwei Adressen vergleichen, die offensichtlich niemals gleich wären, da es nicht möglich ist, zwei Werte an derselben Stelle zu speichern.
if (first == second) /* Strings können niemals verglichen werden */
Im folgenden Beispiel wird die strcmp-Funktion verwendet, um zwei Zeichenfolgen zu vergleichen:
#include <string.h>
int main()
{
char first[80], second[80];
int t;
for(t=1;t<=2;t++)
{
printf("\nEnter a string: ");
gets(first);
printf("Enter another string: ");
gets(second);
if (strcmp(first, second) == 0)
puts("The two strings are equal");
else
puts("The two strings are not equal");
}
return 0;
}
Und die Ausführung des Programms wird wie folgt sein:
Enter a string: Tarun
Enter another string: tarun
The two strings are not equal
Enter a string: Tarun
Enter another string: Tarun
The two strings are equal
Die strcat-Funktion
Die strcat-Funktion wird verwendet, um einen String mit einem anderen zu verbinden. Lassen Sie uns sehen, wie? Am Beispiel:
#include <string.h>
int main()
{
char first[80], second[80];
printf("Enter a string: ");
gets(first);
printf("Enter another string: ");
gets(second);
strcat(first, second);
printf("The two strings joined together: %s\n",
first);
return 0;
}
Und die Ausführung des Programms wird wie folgt sein:
Enter a string: Data
Enter another string: Recovery
The two strings joined together: DataRecovery
Die strtok-Funktion
Die strtok-Funktion 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 ein Wort anhand der Trennzeichen, Leerzeichen, Tabulator und Zeilenumbruch. Jedes Wort wird dann in einer separaten Zeile angezeigt:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *in;
char line[80];
char *delimiters = " \t\n";
char *token;
if ((in = fopen("C:\\text.txt", "r")) == NULL)
{
puts("Unable to open the input file");
return 0;
}
/* Read each line one at a time */
while(!feof(in))
{
/* Get one line */
fgets(line, 80, in);
if (!feof(in))
{
/* Break the line up into words */
token = strtok(line, delimiters);
while (token != NULL)
{
puts(token);
/* Get the next word */
token = strtok(NULL, delimiters);
}
}
}
fclose(in);
return 0;
}
Das obige Programm, in = fopen("C:\\text.txt", "r"), öffnet die vorhandene Datei C:\\text.txt. Wenn die im angegebenen Pfad nicht vorhanden ist oder die Datei 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()
{
char line[100], *sub_text;
/* initialize string */
strcpy(line,"hello, I am a string;");
printf("Line: %s\n", line);
/* add to end of string */
strcat(line," what are you?");
printf("Line: %s\n", line);
/* find length of string */
/* strlen brings back */
/* length as type size_t */
printf("Length of line: %d\n", (int)strlen(line));
/* find occurence of substrings */
if ( (sub_text = strchr ( line, 'W' ) )!= NULL )
printf("String starting with \"W\" ->%s\n",
sub_text);
if ( ( sub_text = strchr ( line, 'w' ) )!= NULL )
printf("String starting with \"w\" ->%s\n",
sub_text);
if ( ( sub_text = strchr ( sub_text, 'u' ) )!= NULL )
printf("String starting with \"w\" ->%s\n",
sub_text);
}
Die Ausgabe des Programms wird wie folgt angezeigt:
Line: hello, I am a string;
Line: hello, I am a string; what are you?
Length of line: 35
String starting with "w" ->what are you?
String starting with "w" ->u?
Funktionen
Der beste Weg, ein großes Programm zu entwickeln und zu pflegen, besteht darin, es aus kleineren Teilen zusammenzusetzen, von denen jedes einfacher zu handhaben ist (eine Technik, die manchmal als Teile und Herrsche bezeichnet wird). Funktionen ermöglichen es dem Programmierer, das Programm zu modularisieren.
Functions allow complicated programs to be parceled up into small blocks, each of which is easier to write, read, and maintain. We have already encountered the function main and made use of printf from the standard library. We can of course make our own functions and header files. A function has the following layout:
return-type function-name ( argument list if necessary )
{
local-declarations;
statements ;
return return-value;
}
If return-type is omitted, C defaults to int. The return-value must be of the declared type. All variables declared within functions are called local variables, in that they are known only in the function to which they have been defined.
Some functions have a parameter list that provides a communication method between the function, and the module that called the function. The parameters are also local variables, in that they are not available outside of the function. The programs covered so far all have main, which is a function.
A function may simply perform a task without returning any value, in which case it has the following layout:
void function-name ( argument list if necessary )
{
local-declarations ;
statements;
}
Arguments are always passed by value in C function calls. This means that local copies of the values of the arguments are passed to the routines. Any change made to the arguments internally in the function is made only to the local copies of the arguments.
In order to change or define an argument in the argument list, this argument must be passed as an address. You use regular variables if the function does not change the values of those arguments. You MUST use pointers if the function changes the values of those arguments.
Let us learn with examples:
#include <stdio.h>
void exchange ( int *a, int *b )
{
int temp;
temp = *a;
*a = *b;
*b = temp;
printf(" From function exchange: ");
printf("a = %d, b = %d\n", *a, *b);
}
void main()
{
int a, b;
a = 5;
b = 7;
printf("From main: a = %d, b = %d\n", a, b);
exchange(&a, &b);
printf("Back in main: ");
printf("a = %d, b = %d\n", a, b);
}
And the output of this program will be displayed as follows:
From main: a = 5, b = 7
From function exchange: a = 7, b = 5
Back in main: a = 7, b = 5
Let us see another example. The following example uses a function called square which writes the square of the numbers between 1 and 10.
#include <stdio.h>
int square(int x); /* Function prototype */
int main()
{
int counter;
for (counter=1; counter<=10; counter++)
printf("Square of %d is %d\n", counter, square(counter));
return 0;
}
/* Define the function 'square' */
int square(int x)
{
return x * x;
}
The output of this program will be displayed as follows:
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
Square of 6 is 36
Square of 7 is 49
Square of 8 is 64
Square of 9 is 81
Square of 10 is 100
The function prototype square declares a function that takes an integer parameter and returns an integer. When the compiler reaches the function call to square in the main program, it is able to check the function call against the function's definition.
When the program reaches the line that calls the function square, the program jumps to the function and executes that function before resuming its path through the main program. Programs that do not have a return type should be declared using void. Thus Parameters to the function may be Pass By Value or Pass By Reference.
A Recursive function is a function that calls itself. And this process is called recursion.
Pass By Value Functions
The square function's parameters in the previous example are passed by value. This means that only a copy of the variable has been passed to the function. Any changes to the value will not be reflected back to the calling function.
The following example uses pass-by-value and changes the value of the passed parameter, which has no effect on the calling function. The function count_down has been declared as void as there is no return type.
#include <stdio.h>
void count_down(int x);
int main()
{
int counter;
for (counter=1; counter<=10; counter++)
count_down(counter);
return 0;
}
void count_down(int x)
{
int counter;
for (counter = x; counter > 0; counter--)
{
printf("%d ", x);
x--;
}
putchar('\n');
}
The output of the program will be displayed as follows:
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
Let us see another C Pass By Value Example to better understand it. The following example converts a number between 1 and 30,000 typed by the user into words.
#include <stdio.h>
void do_units(int num);
void do_tens(int num);
void do_teens(int num);
int main()
{
int num, residue;
do
{
printf("Enter a number between 1 and 30,000: ");
scanf("%d", &num);
} while (num < 1 || num > 30000);
residue = num;
printf("%d in words = ", num);
do_tens(residue/1000);
if (num >= 1000)
printf("thousand ");
residue %= 1000;
do_units(residue/100);
if (residue >= 100)
{
printf("hundred ");
}
if (num > 100 && num%100 > 0)
printf("and ");
residue %=100;
do_tens(residue);
putchar('\n');
return 0;
}
void do_units(int num)
{
switch(num)
{
case 1:
printf("one ");
break;
case 2:
printf("two ");
break;
case 3:
printf("three ");
break;
case 4:
printf("four ");
break;
case 5:
printf("five ");
break;
case 6:
printf("six ");
break;
case 7:
printf("seven ");
break;
case 8:
printf("eight ");
break;
case 9:
printf("nine ");
}
}
void do_tens(int num)
{
switch(num/10)
{
case 1:
do_teens(num);
break;
case 2:
printf("twenty ");
break;
case 3:
printf("thirty ");
break;
case 4:
printf("forty ");
break;
case 5:
printf("fifty ");
break;
case 6:
printf("sixty ");
break;
case 7:
printf("seventy ");
break;
case 8:
printf("eighty ");
break;
case 9:
printf("ninety ");
}
if (num/10 != 1)
do_units(num%10);
}
void do_teens(int num)
{
switch(num)
{
case 10:
printf("ten ");
break;
case 11:
printf("eleven ");
break;
case 12:
printf("twelve ");
break;
case 13:
printf("thirteen ");
break;
case 14:
printf("fourteen ");
break;
case 15:
printf("fifteen ");
break;
case 16:
printf("sixteen ");
break;
case 17:
printf("seventeen ");
break;
case 18:
printf("eighteen ");
break;
case 19:
printf("nineteen ");
}
}
and the output of the program will be as follows:
Enter a number between 1 and 30,000: 12345
12345 in words = twelve thousand three hundred and forty five
Call-by-reference
To make a function call-by-reference, instead of passing the variable itself, pass the address of the variable. The address of the variable can be taken by using the & operator. The following calls a swap function passing the address of variables instead of the actual values.
swap(&x, &y);
Dereferencing
Das Problem, das wir jetzt haben, ist, dass der Funktion swap die Adresse und nicht die Variable übergeben wurde, also müssen wir die Variablen dereferenzieren, damit wir die tatsächlichen Werte und nicht die Adressen der Variablen zum Austauschen betrachten sie.
Die Dereferenzierung wird in C durch die Verwendung der Pointer (*)-Notation erreicht. Einfach ausgedrückt bedeutet dies, vor jeder Variablen ein * zu setzen, bevor sie verwendet wird, damit es sich auf den Wert der Variablen und nicht auf ihre Adresse bezieht. Das folgende Programm veranschaulicht die Referenzübergabe zum Austauschen zweier Werte.
#include <stdio.h>
void swap(int *x, int *y);
int main()
{
int x=6, y=10;
printf("Before the function swap, x = %d and y =
%d\n\n", x, y);
swap(&x, &y);
printf("After the function swap, x = %d and y =
%d\n\n", x, y);
return 0;
}
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
Sehen wir uns die Ausgabe des Programms an:
Before the function swap, x = 6 and y = 10
After the function swap, x = 10 and y = 6
Funktionen können rekursiv sein, dh eine Funktion kann sich selbst aufrufen. Jeder Aufruf an sich selbst erfordert, dass der aktuelle Zustand der Funktion auf den Stack geschoben wird. Es ist wichtig, sich an diese Tatsache zu erinnern, da es leicht ist, einen Stack-Überlauf zu erzeugen, d. h. der Stack hat keinen Platz mehr, um weitere Daten zu platzieren.
Das folgende Beispiel berechnet die Fakultät einer Zahl unter Verwendung von Rekursion. Eine Fakultät ist eine Zahl, die mit jeder anderen ganzen Zahl unter sich selbst bis hinunter zu 1 multipliziert wird. Zum Beispiel ist die Fakultät der Zahl 6:
Fakultät 6 = 6 * 5 * 4 * 3 * 2 * 1
Daher ist die Fakultät von 6 720. Aus dem obigen Beispiel ist ersichtlich, dass Fakultät 6 = 6 * Fakultät 5 ist. Ebenso ist Fakultät 5 = 5 * Fakultät 4 und so weiter.
Das Folgende ist 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, sie mit Hilfe des Beispiels besser zu verstehen:
#include <stdio.h>
long int factorial(int num);
int main()
{
int num;
long int f;
printf("Enter a number: ");
scanf("%d", &num);
f = factorial(num);
printf("factorial of %d is %ld\n", num, f);
return 0;
}
long int factorial(int num)
{
if (num == 1)
return 1;
else
return num * factorial(num-1);
}
Sehen wir uns die Ausgabe der Ausführung dieses Programms an:
Enter a number: 7
factorial of 7 is 5040
Speicherzuordnung in C
Der C-Compiler hat eine Speicherzuweisungsbibliothek, die in malloc.h definiert ist. Speicher wird mit der malloc-Funktion reserviert und gibt einen Zeiger auf die Adresse zurück. Es braucht einen Parameter, die benötigte Speichergröße in Bytes.
Das folgende Beispiel weist Platz für die Zeichenfolge "hello world" zu.
ptr = (char *)malloc(strlen("Hallo Welt") + 1);
Das zusätzliche Byte wird benötigt, um das Endzeichen der Zeichenfolge, '\0', zu berücksichtigen. Das (char *) wird Cast genannt und zwingt den Rückgabetyp auf char *.
Da Datentypen unterschiedliche Größen haben und malloc den Speicherplatz in Bytes zurückgibt, ist es aus Portabilitätsgründen empfehlenswert, den sizeof-Operator zu verwenden, wenn eine zuzuweisende Größe angegeben wird.
Das folgende Beispiel liest eine Zeichenkette in den Zeichen-Array-Puffer und weist dann genau die erforderliche Speichermenge zu und kopiert sie in eine Variable namens "ptr".
#include <string.h>
#include <malloc.h>
int main()
{
char *ptr, buffer[80];
printf("Enter a string: ");
gets(buffer);
ptr = (char *)malloc((strlen(buffer) + 1) *
sizeof(char));
strcpy(ptr, buffer);
printf("You entered: %s\n", ptr);
return 0;
}
Die Ausgabe des Programms sieht wie folgt aus:
Enter a string: India is the best
You entered: India is the best
Speicher neu zuweisen
Es ist oft möglich, dass Sie während des Programmierens Speicher neu zuweisen möchten. Dies geschieht mit der realloc-Funktion. Die realloc-Funktion benötigt zwei Parameter, die Basisadresse des Speichers, dessen Größe Sie ändern möchten, und die Menge an Speicherplatz, die Sie reservieren möchten, und gibt einen Zeiger auf die Basisadresse zurück.
Angenommen, wir haben Platz für einen Zeiger namens msg reserviert und möchten Platz auf die Menge an Platz neu zuweisen, die er bereits einnimmt, plus die Länge eines anderen Strings, dann könnten wir Folgendes verwenden.
msg = (char *)realloc(msg, (strlen(msg) + strlen(buffer) + 1)*sizeof(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 stoppt das Lesen von Zeichenfolgen, wenn eine leere Zeichenfolge eingegeben wird.
#include <string.h>
#include <malloc.h>
int main()
{
char buffer[80], *msg;
int firstTime=0;
do
{
printf("\nEnter a sentence: ");
gets(buffer);
if (!firstTime)
{
msg = (char *)malloc((strlen(buffer) + 1) *
sizeof(char));
strcpy(msg, buffer);
firstTime = 1;
}
else
{
msg = (char *)realloc(msg, (strlen(msg) +
strlen(buffer) + 1) * sizeof(char));
strcat(msg, buffer);
}
puts(msg);
} while(strcmp(buffer, ""));
free(msg);
return 0;
}
Die Ausgabe des Programms sieht wie folgt aus:
Enter a sentence: Once upon a time
Once upon a time
Enter a sentence: there was a king
Once upon a timethere was a king
Enter a sentence: the king was
Once upon a timethere was a kingthe king was
Enter a sentence:
Once upon a timethere was a kingthe king was
Erinnerung loslassen
Wenn Sie mit dem zugewiesenen Speicher fertig sind, sollten Sie niemals vergessen, den Speicher freizugeben, da dies Ressourcen freigibt und die Geschwindigkeit verbessert. Verwenden Sie die Free-Funktion, um zugewiesenen Speicher freizugeben.
free(ptr);
Strukturen
Zusätzlich zu den grundlegenden Datentypen verfügt C über einen Strukturmechanismus, der es Ihnen ermöglicht, miteinander verwandte 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 den 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.
struct month
{
char name[10];
char abbrev[4];
int days;
};
Ein Strukturtyp wird normalerweise am Anfang einer Datei mit einer typedef-Anweisung definiert. typedef definiert und benennt einen neuen Typ, was seine Verwendung im gesamten Programm ermöglicht. typedef treten normalerweise direkt nach den #define- und #include-Anweisungen in einer Datei auf.
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, die Typedef in Großbuchstaben zu benennen. Hier sind die Beispiele für die Strukturdefinition.
typedef struct {
char name[64];
char course[128];
int age;
int year;
} student;
Dies definiert einen neuen Typ Student. Variablen vom Typ Student können wie folgt deklariert werden.
student st_rec;
Beachten Sie, wie ähnlich dies der Deklaration von int oder float ist. Der Variablenname ist st_rec, er hat Elemente namens Name, Kurs, Alter und Jahr. Ebenso
typedef struct element
{
char data;
struct element *next;
} STACKELEMENT;
A variable of the user defined type struct element may now be declared as follows.
STACKELEMENT *stack;
Betrachten Sie die folgende Struktur:
struct student
{
char *name;
int grade;
};
Ein Zeiger auf struct student kann wie folgt definiert werden.
struct student *hnc;
When accessing a pointer to a structure, the member pointer operator, -> is used instead of the dot operator. To add a grade to a structure,
s.grade = 50;
Sie könnten der Struktur wie folgt eine Note zuweisen.
s->grade = 50;
Wie bei den grundlegenden Datentypen müssen Sie, wenn Sie möchten, dass die in einer Funktion an übergebenen Parametern vorgenommenen Änderungen dauerhaft sind, eine Referenz übergeben (die Adresse übergeben). Der Mechanismus ist genau derselbe wie bei den grundlegenden Datentypen. Übergeben Sie die Adresse und verweisen Sie in Zeigernotation auf die Variable.
Nachdem Sie die Struktur definiert haben, können Sie eine Instanz davon deklarieren und den Elementen unter Verwendung der Punktnotation Werte zuweisen. Das folgende Beispiel veranschaulicht die Verwendung der Monatsstruktur.
#include <stdio.h>
#include <string.h>
struct month
{
char name[10];
char abbreviation[4];
int days;
};
int main()
{
struct month m;
strcpy(m.name, "January");
strcpy(m.abbreviation, "Jan");
m.days = 31;
printf("%s is abbreviated as %s and has %d days\n", m.name, m.abbreviation, m.days);
return 0;
}
Die Ausgabe des Programms sieht wie folgt aus:
January is abbreviated as Jan and has 31 days
Alle ANSI-C-Compiler erlauben es Ihnen, eine Struktur einer anderen zuzuweisen, indem sie eine Member-weise Kopie durchführen. Wenn wir Monatsstrukturen namens m1 und m2 hätten, könnten wir die Werte von m1 bis m2 wie folgt zuweisen:
- Struktur mit Pointer-Mitgliedern.
- Struktur wird initialisiert.
- Eine Struktur an eine Funktion übergeben.
- Zeiger und Strukturen.
Strukturen mit Zeigerelementen in C
Das Halten von Strings in einem Array fester Größe ist eine ineffiziente Nutzung des Speichers. Ein effizienterer Ansatz wäre die Verwendung von Zeigern. Zeiger werden in Strukturen genauso verwendet wie in normalen Zeigerdefinitionen. Sehen wir uns ein Beispiel an:
#include <string.h>
#include <malloc.h>
struct month
{
char *name;
char *abbreviation;
int days;
};
int main()
{
struct month m;
m.name = (char *)malloc((strlen("January")+1) *
sizeof(char));
strcpy(m.name, "January");
m.abbreviation = (char *)malloc((strlen("Jan")+1) *
sizeof(char));
strcpy(m.abbreviation, "Jan");
m.days = 31;
printf("%s is abbreviated as %s and has %d days\n",
m.name, m.abbreviation, m.days);
return 0;
}
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 der Deklarationsanweisung Initialisierer hinzugefügt werden. Da Monate bei 1 beginnen, Arrays in C jedoch bei Null beginnen, wurde im folgenden Beispiel ein zusätzliches Element an Position Null namens Junk verwendet.
#include <stdio.h>
#include <string.h>
struct month
{
char *name;
char *abbreviation;
int days;
} month_details[] =
{
"Junk", "Junk", 0,
"January", "Jan", 31,
"February", "Feb", 28,
"March", "Mar", 31,
"April", "Apr", 30,
"May", "May", 31,
"June", "Jun", 30,
"July", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"October", "Oct", 31,
"November", "Nov", 30,
"December", "Dec", 31
};
int main()
{
int counter;
for (counter=1; counter<=12; counter++)
printf("%s is abbreviated as %s and has %d days\n",
month_details[counter].name,
month_details[counter].abbreviation,
month_details[counter].days);
return 0;
}
Und die Ausgabe wird wie folgt angezeigt:
January is abbreviated as Jan and has 31 days
February is abbreviated as Feb and has 28 days
March is abbreviated as Mar and has 31 days
April is abbreviated as Apr and has 30 days
May is abbreviated as May and has 31 days
June is abbreviated as Jun and has 30 days
July is abbreviated as Jul and has 31 days
August is abbreviated as Aug and has 31 days
September is abbreviated as Sep and has 30 days
October is abbreviated as Oct and has 31 days
November is abbreviated as Nov and has 30 days
December is abbreviated as Dec and has 31 days
Übergeben von Strukturen an Funktionen in C
Strukturen können als Parameter an eine Funktion übergeben werden, genau wie alle grundlegenden Datentypen. Das folgende Beispiel verwendet eine Struktur namens date, 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>
struct month
{
char *name;
char *abbreviation;
int days;
} month_details[] =
{
"Junk", "Junk", 0,
"January", "Jan", 31,
"February", "Feb", 28,
"March", "Mar", 31,
"April", "Apr", 30,
"May", "May", 31,
"June", "Jun", 30,
"July", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"October", "Oct", 31,
"November", "Nov", 30,
"December", "Dec", 31
};
struct date
{
int day;
int month;
int year;
};
int isLeapYear(struct date d);
int main()
{
struct date d;
printf("Enter the date (eg: 11/11/1980): ");
scanf("%d/%d/%d", &d.day, &d.month, &d.year);
printf("The date %d %s %d is ", d.day,
month_details[d.month].name, d.year);
if (isLeapYear(d) == 0)
printf("not ");
puts("a leap year");
return 0;
}
int isLeapYear(struct date d)
{
if ((d.year % 4 == 0 && d.year % 100 != 0) ||
d.year % 400 == 0)
return 1;
return 0;
}
Und die Ausführung des Programms wird wie folgt sein:
Enter the date (eg: 11/11/1980): 9/12/1980
The date 9 December 1980 is a leap year
Im folgenden Beispiel wird ein Array von Strukturen dynamisch zugewiesen, um die Namen und Noten der Schüler zu speichern. Die Noten werden dem Benutzer dann in aufsteigender Reihenfolge angezeigt.
#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);
}
Die Ausführung der Ausgabe wird wie folgt sein:
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
Eine Vereinigung ermöglicht es Ihnen, dieselben Daten mit unterschiedlichen Typen zu betrachten oder dieselben Daten mit unterschiedlichen Namen zu verwenden. Gewerkschaften ähneln Strukturen. Eine Union wird genauso deklariert und verwendet wie eine Struktur.
Eine Union unterscheidet sich von einer Struktur dadurch, dass jeweils nur eines ihrer Mitglieder verwendet werden kann. Der Grund dafür ist einfach. Alle Mitglieder einer Gewerkschaft belegen denselben Speicherbereich. Sie werden übereinander gelegt.
Unions werden auf die gleiche Weise wie Strukturen definiert und deklariert. Der einzige Unterschied in den Deklarationen besteht darin, dass anstelle von struct das Schlüsselwort union verwendet wird. Um eine einfache Vereinigung einer char-Variablen und einer Integer-Variablen zu definieren, würden Sie Folgendes schreiben:
union shared {
char c;
int i;
};
Diese Union, gemeinsam genutzt, kann verwendet werden, um Instanzen einer Union zu erstellen, die entweder einen Zeichenwert c oder einen ganzzahligen Wert i enthalten kann. Dies ist eine ODER-Bedingung. Im Gegensatz zu einer Struktur, die beide Werte enthalten würde, kann die Vereinigung jeweils nur einen Wert enthalten.
Eine Union kann bei ihrer Deklaration initialisiert werden. Denn es kann immer nur ein Member verwendet und nur einer initialisiert werden. Um Verwirrung zu vermeiden, kann nur das erste Mitglied der Union initialisiert werden. Der folgende Code zeigt eine Instanz der Shared Union, die deklariert und initialisiert wird:
Union Shared Generic_Variable = {`@'};
Beachten Sie, dass die union generic_variable genauso initialisiert wurde, wie das erste Mitglied einer Struktur initialisiert würde.
Einzelne Union-Mitglieder können auf die gleiche Weise wie Strukturmitglieder verwendet werden, indem der Elementoperator (.) verwendet wird. Es gibt jedoch einen wichtigen Unterschied beim Zugriff auf Gewerkschaftsmitglieder.
Es sollte immer nur auf ein Gewerkschaftsmitglied zugegriffen werden. Da eine Gewerkschaft ihre Mitglieder übereinander speichert, ist es wichtig, immer nur auf ein Mitglied gleichzeitig zuzugreifen.
Das union-Schlüsselwort
union tag {
union_member(s);
/* additional statements may go here */
}instance;
Das Schlüsselwort union wird zum Deklarieren von Unions verwendet. Eine Union ist eine Sammlung von einer oder mehreren Variablen (union_members), die unter einem einzigen Namen gruppiert wurden. Außerdem belegt jedes dieser Unionsmitglieder den gleichen Speicherbereich.
Das Schlüsselwort union identifiziert den Beginn einer Union-Definition. Es folgt ein Tag, das der Name ist, der der Union gegeben wurde. Dem Tag folgen die in geschweiften Klammern eingeschlossenen Union-Mitglieder.
Eine Instanz, die eigentliche Deklaration einer Vereinigung, kann ebenfalls definiert werden. Wenn Sie die Struktur ohne die Instanz definieren, ist sie nur eine Vorlage, die später in einem Programm zum Deklarieren von Strukturen verwendet werden kann. Das Folgende ist das Format einer Vorlage:
union tag {
union_member(s);
/* additional statements may go here */
};
Um die Vorlage zu verwenden, würden Sie das folgende Format verwenden:
Union-Tag-Instanz;
Um dieses Format zu verwenden, müssen Sie zuvor eine Vereinigung mit dem angegebenen Tag deklariert haben.
/* Declare a union template called tag */
union tag {
int num;
char alps;
}
/* Use the union template */
union tag mixed_variable;
/* Declare a union and instance together */
union generic_type_tag {
char c;
int i;
float f;
double d;
} generic;
/* Initialize a union. */
union date_tag {
char full_date[9];
struct part_date_tag {
char month[2];
char break_value1;
char day[2];
char break_value2;
char year[2];
} part_date;
}date = {"09/12/80"};
Lassen Sie es uns anhand von Beispielen besser verstehen:
#include <stdio.h>
int main()
{
union
{
int value; /* This is the first part of the union */
struct
{
char first; /* These two values are the second part of it */
char second;
} half;
} number;
long index;
for (index = 12 ; index < 300000L ; index += 35231L)
{
number.value = index;
printf("%8x %6x %6x\n", number.value,
number.half.first,
number.half.second);
}
return 0;
}
Und die Ausgabe des Programms wird wie folgt angezeigt:
c c 0
89ab ffab ff89
134a 4a 13
9ce9 ffe9 ff9c
2688 ff88 26
b027 27 ffb0
39c6 ffc6 39
c365 65 ffc3
4d04 4 4d
Eine praktische Verwendung einer Union bei der Datenwiederherstellung
Sehen wir uns nun eine praktische Verwendung von Union an, nämlich die Datenwiederherstellungsprogrammierung. 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 nicht das vollständige Modell einer Software zum Scannen fehlerhafter Sektoren.
Untersuchen wir das Programm:
#include<dos.h>
#include<conio.h>
int main()
{
int rp, head, track, sector, status;
char *buf;
union REGS in, out;
struct SREGS s;
clrscr();
/* Reset the disk system to initialize to disk */
printf("\n Resetting the disk system....");
for(rp=0;rp<=2;rp++)
{
in.h.ah = 0;
in.h.dl = 0x00;
int86(0x13,&in,&out);
}
printf("\n\n\n Now Testing the Disk for Bad Sectors....");
/* scan for bad sectors */
for(track=0;track<=79;track++)
{
for(head=0;head<=1;head++)
{
for(sector=1;sector<=18;sector++)
{
in.h.ah = 0x04;
in.h.al = 1;
in.h.dl = 0x00;
in.h.ch = track;
in.h.dh = head;
in.h.cl = sector;
in.x.bx = FP_OFF(buf);
s.es = FP_SEG(buf);
int86x(0x13,&in,&out,&s);
if(out.x.cflag)
{
status=out.h.ah;
printf("\n track:%d Head:%d Sector:%d Status ==0x%X",track,head,sector,status);
}
}
}
}
printf("\n\n\nDone");
return 0;
}
Lassen Sie uns nun sehen, wie die Ausgabe aussehen wird, wenn es einen fehlerhaften Sektor auf der Diskette gibt:
Festplattensystem zurücksetzen....
Die Festplatte wird jetzt auf fehlerhafte Sektoren getestet....
track:0 Head:0 Sector:4 Status ==0xA
track:0 Head:0 Sector:5 Status ==0xA
track:1 Head:0 Sector:4 Status ==0xA
track:1 Head:0 Sector:5 Status ==0xA
track:1 Head:0 Sector:6 Status ==0xA
track:1 Head:0 Sector:7 Status ==0xA
track:1 Head:0 Sector:8 Status ==0xA
track:1 Head:0 Sector:11 Status ==0xA
track:1 Head:0 Sector:12 Status ==0xA
track:1 Head:0 Sector:13 Status ==0xA
track:1 Head:0 Sector:14 Status ==0xA
track:1 Head:0 Sector:15 Status ==0xA
track:1 Head:0 Sector:16 Status ==0xA
track:1 Head:0 Sector:17 Status ==0xA
track:1 Head:0 Sector:18 Status ==0xA
track:1 Head:1 Sector:5 Status ==0xA
track:1 Head:1 Sector:6 Status ==0xA
track:1 Head:1 Sector:7 Status ==0xA
track:1 Head:1 Sector:8 Status ==0xA
track:1 Head:1 Sector:9 Status ==0xA
track:1 Head:1 Sector:10 Status ==0xA
track:1 Head:1 Sector:11 Status ==0xA
track:1 Head:1 Sector:12 Status ==0xA
track:1 Head:1 Sector:13 Status ==0xA
track:1 Head:1 Sector:14 Status ==0xA
track:1 Head:1 Sector:15 Status ==0xA
track:1 Head:1 Sector:16 Status ==0xA
track:1 Head:1 Sector:17 Status ==0xA
track:1 Head:1 Sector:18 Status ==0xA
track:2 Head:0 Sector:4 Status ==0xA
track:2 Head:0 Sector:5 Status ==0xA
track:14 Head:0 Sector:6 Status ==0xA
Done
Es kann ein bisschen schwierig sein, die Funktionen und Interrupts zu verstehen, die in diesem Programm verwendet werden, um die Festplatte auf fehlerhafte Sektoren zu überprüfen und das Festplattensystem zurückzusetzen usw., aber Sie brauchen sich keine Sorgen zu machen, wir werden all diese Dinge im BIOS lernen und Programmierabschnitte später in den nächsten kommenden Kapiteln zu unterbrechen.
Dateiverwaltung in C
Dateizugriff in C wird erreicht, indem ein Stream mit einer Datei verknüpft wird. C kommuniziert mit Dateien über einen neuen Datentyp namens Dateizeiger. Dieser Typ wird in stdio.h definiert und als FILE * geschrieben. Ein Dateizeiger namens output_file wird in einer Anweisung wie
deklariert
FILE *output_file;
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. Sie werden fopen normalerweise wie folgt verwenden
if ((output_file = fopen("output_file", "w")) == NULL)
fprintf(stderr, "Cannot open %s\n",
"output_file");
fopen nimmt zwei Argumente, beide sind Zeichenketten, das erste ist der Name der zu öffnenden Datei, das zweite ist ein Zugriffszeichen, das normalerweise eines von r, a oder w usw. ist. Dateien können in einer Nummer geöffnet werden von Modi, wie in der folgenden Tabelle gezeigt.
File Modes |
r |
Open a text file for reading. |
w |
Create a text file for writing. If the file exists, it is overwritten. |
a |
Open a text file in append mode. Text is added to the end of the file. |
rb |
Open a binary file for reading. |
wb |
Create a binary file for writing. If the file exists, it is overwritten. |
ab |
Open a binary file in append mode. Data is added to the end of the file. |
r+ |
Open a text file for reading and writing. |
w+ |
Create a text file for reading and writing. If the file exists, it is overwritten. |
a+ |
Open a text file for reading and writing at the end. |
r+b or rb+ |
Open binary file for reading and writing. |
w+b or wb+ |
Create a binary file for reading and writing. If the file exists, it is overwritten. |
a+b or ab+ |
Open a text file for reading and writing at the end. |
Die Update-Modi werden mit fseek, fsetpos und Rewind-Funktionen verwendet. Die Funktion fopen gibt einen Dateizeiger oder NULL zurück, wenn ein Fehler auftritt.
Das folgende Beispiel öffnet eine Datei, tarun.txt, im schreibgeschützten Modus. Es ist eine gute Programmierpraxis, zu testen, ob die Datei existiert.
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
Schließen von Dateien
Dateien werden mit der Funktion fclose geschlossen. Die Syntax lautet wie folgt:
fclose(in);
Dateien lesen
Die Funktion feof wird verwendet, um das Ende der Datei zu testen. Die Funktionen fgetc, fscanf und fgets werden verwendet, um Daten aus der Datei zu lesen.
Das folgende Beispiel listet den Inhalt einer Datei auf dem Bildschirm auf, wobei fgetc verwendet wird, um die Datei zeichenweise zu lesen.
#include <stdio.h>
int main()
{
FILE *in;
int key;
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
while (!feof(in))
{
key = fgetc(in);
/* The last character read is the end of file marker so don't print it */
if (!feof(in))
putchar(key);
}
fclose(in);
return 0;
}
Die fscanf-Funktion kann verwendet werden, um verschiedene Datentypen aus der Datei zu lesen, wie im folgenden Beispiel, vorausgesetzt, die Daten in der Datei haben das Format der Formatzeichenfolge, die mit fscanf verwendet wird.
fscanf(in, "%d/%d/%d", &Tag, &Monat, &Jahr);
Die Funktion fgets wird verwendet, um eine Anzahl von Zeichen aus einer Datei zu lesen. stdin ist der Standard-Eingabedatei-Stream, und die fgets-Funktion kann verwendet werden, um die Eingabe zu steuern.
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()
{
FILE *in, *out;
int key;
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
out = fopen("copy.txt", "w");
while (!feof(in))
{
key = fgetc(in);
if (!feof(in))
fputc(key, out);
}
fclose(in);
fclose(out);
return 0;
}
Die Funktion fprintf kann verwendet werden, um formatierte Daten in eine Datei zu schreiben.
fprintf(out, "Date: %02d/%02d/%02d\n",
day, month, year);
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 erlaubt die Übergabe von Argumenten über die Kommandozeile. Der Parameter argc ist ein Argumentzähler und enthält die Anzahl der von der Kommandozeile übergebenen Parameter. Der Parameter argv ist der Argumentvektor, der ein Array von Zeigern auf Zeichenfolgen ist, die die tatsächlich übergebenen Parameter darstellen.
Das folgende Beispiel erlaubt die Übergabe einer beliebigen Anzahl von Argumenten von der Befehlszeile und gibt sie aus. argv[0] ist das eigentliche Programm. Das Programm muss von einer Eingabeaufforderung ausgeführt werden.
#include <stdio.h>
int main(int argc, char **argv)
{
int counter;
puts("The arguments to the program are:");
for (counter=0; counter<argc; counter++)
puts(argv[counter]);
return 0;
}
If the program name was count.c, it could be called as follows from the command line.
count 3
or
count 7
or
count 192 etc.
Das nächste Beispiel verwendet die Dateibehandlungsroutinen, um eine Textdatei in eine neue Datei zu kopieren. Beispielsweise könnte das Befehlszeilenargument wie folgt aufgerufen werden:
txtcpy eins.txt zwei.txt
#include <stdio.h>
int main(int argc, char **argv)
{
FILE *in, *out;
int key;
if (argc < 3)
{
puts("Usage: txtcpy source destination\n");
puts("The source must be an existing file");
puts("If the destination file exists, it will be
overwritten");
return 0;
}
if ((in = fopen(argv[1], "r")) == NULL)
{
puts("Unable to open the file to be copied");
return 0;
}
if ((out = fopen(argv[2], "w")) == NULL)
{
puts("Unable to open the output file");
return 0;
}
while (!feof(in))
{
key = fgetc(in);
if (!feof(in))
fputc(key, out);
}
fclose(in);
fclose(out);
return 0;
}
Bitweise Manipulatoren
Auf Hardwareebene werden Daten als Binärzahlen dargestellt. Die binäre Darstellung der Zahl 59 ist 111011. Bit 0 ist das niedrigstwertige Bit, und in diesem Fall ist Bit 5 das höchstwertige Bit.
Jeder Bitsatz wird als 2 hoch dem Bitsatz berechnet. Mit bitweisen Operatoren können Sie Integer-Variablen auf Bitebene manipulieren. Das Folgende zeigt die binäre Darstellung der Zahl 59.
binary representation of the number 59 |
bit 5 4 3 2 1 0 |
2 power n 32 16 8 4 2 1 |
set 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ären Form.
Binary Digits |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
Die folgende Tabelle listet die bitweisen Operatoren auf, die zur Manipulation von Binärzahlen verwendet werden können.
Binary Digits |
& |
Bitwise AND |
| |
Bitwise OR |
^ |
Bitwise Exclusive OR |
~ |
Bitwise Complement |
<< |
Bitwise Shift Left |
>> |
Bitwise Shift Right |
Bitweises UND
Das bitweise UND ist nur wahr, wenn beide Bits gesetzt sind. Das folgende Beispiel zeigt das Ergebnis eines bitweisen AND auf die Zahlen 23 und 12.
10111 (23)
01100 (12) AND
____________________
00100 (result = 4) |
Sie können einen Maskenwert verwenden, um zu prüfen, ob bestimmte Bits gesetzt wurden. Wenn wir prüfen wollten, ob die Bits 1 und 3 gesetzt sind, könnten wir die Zahl mit 10 (dem Wert von Bit 1 und 3) maskieren und das Ergebnis gegen die Maske testen.
#include <stdio.h>
int main()
{
int num, mask = 10;
printf("Enter a number: ");
scanf("%d", &num);
if ((num & mask) == mask)
puts("Bits 1 and 3 are set");
else
puts("Bits 1 and 3 are not set");
return 0;
}
Bitweises ODER
Das bitweise ODER ist wahr, wenn eines der Bits gesetzt ist. Das Folgende zeigt das Ergebnis einer bitweisen ODER-Verknüpfung der Zahlen 23 und 12.
10111 (23)
01100 (12) OR
______________________
11111 (result = 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, mask = 4;
printf("Enter a number: ");
scanf("%d", &num);
num |= mask;
printf("After ensuring bit 2 is set: %d\n", num);
return 0;
}
Bitweises exklusives ODER
Das bitweise Exklusiv-ODER ist wahr, wenn eines der Bits gesetzt ist, aber nicht beide. Das Folgende zeigt das Ergebnis eines bitweisen Exklusiv-ODER auf den Zahlen 23 und 12.
10111 (23)
01100 (12) Exclusive OR (XOR)
_____________________________
11011 (result = 27) |
Das Exklusiv-ODER hat einige interessante Eigenschaften. Wenn Sie eine Zahl allein mit Exklusiv-ODER verknüpfen, setzt sie sich selbst auf Null, da die Nullen Null bleiben und die Einsen nicht beide gesetzt werden können, also auf Null gesetzt werden.
Wenn Sie also eine Zahl mit einer anderen Zahl exklusiv ODER und dann das Ergebnis mit der anderen Zahl exklusiv ODER erneut mit der anderen Zahl verknüpfen, ist das Ergebnis die ursprüngliche Zahl. Sie können dies mit den im obigen Beispiel verwendeten Nummern versuchen.
23 XOR 12 = 27
27 XOR 12 = 23
27 XOR 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 Zahl zu veranschaulichen.
#include <stdio.h>
int main()
{
int num, key = 23;
printf("Enter a number: ");
scanf("%d", &num);
num ^= key;
printf("Exclusive OR with %d gives %d\n", key, num);
num ^= key;
printf("Exclusive OR with %d gives %d\n", key, num);
return 0;
}
Bitweises Kompliment
Das bitweise Kompliment ist ein Einser-Kompliment-Operator, 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("The compliment of %X is %X\n", num, ~num);
return 0;
}
Bitweise Verschiebung nach links
Der Bitwise Shift Left-Operator verschiebt die Zahl nach links. Die höchstwertigen Bits gehen verloren, wenn sich die Zahl nach links bewegt, und die frei gewordenen niedrigstwertigen Bits sind Null. Das Folgende zeigt die binäre Darstellung von 43.
0101011 (dezimal 43)
Indem wir die Bits nach links verschieben, verlieren wir das höchstwertige Bit (in diesem Fall eine Null) und die Zahl wird am niederwertigsten Bit mit einer Null aufgefüllt. Das Folgende ist die resultierende Zahl.
1010110 (dezimal 86)
Bitweise Verschiebung nach rechts
Der Bitwise Shift Right-Operator verschiebt die Zahl nach rechts. Null wird in die geräumten höchstwertigen Bits eingeführt, und die geräumten niedrigstwertigen Bits gehen verloren. Das Folgende zeigt die binäre Darstellung der Zahl 43.
0101011 (decimal 43)
Indem wir die Bits nach rechts verschieben, verlieren wir das niedrigstwertige Bit (in diesem Fall eine Eins), und die Zahl wird am höchstwertigen Bit mit einer Null aufgefüllt. Das Folgende ist die resultierende Zahl.
0010101 (dezimal 21)
Das folgende Programm verwendet die bitweise Verschiebung nach rechts und das bitweise UND, um eine Zahl als 16-Bit-Binärzahl anzuzeigen. Die Zahl wird sukzessive von 16 nach rechts bis auf Null verschoben und bitweise UND-verknüpft mit 1, um zu sehen, ob das Bit gesetzt ist. Eine alternative Methode wäre die Verwendung aufeinanderfolgender Masken mit dem bitweisen OR-Operator.
#include <stdio.h>
int main()
{
int counter, num;
printf("Enter a number: ");
scanf("%d", &num);
printf("%d is binary: ", num);
for (counter=15; counter>=0; counter--)
printf("%d", (num >> counter) & 1);
putchar('\n');
return 0;
}
Funktionen für Binär-Dezimal-Konvertierungen
Die beiden als nächstes angegebenen Funktionen dienen der Binär-zu-Dezimal- und der Dezimal-zu-Binär-Konvertierung. Die nebenstehende Funktion zum Konvertieren einer Dezimalzahl in eine entsprechende Binärzahl unterstützt bis zu 32-Bit-Binärzahlen. Sie können dieses oder das zuvor angegebene Programm für die Konvertierung gemäß Ihren Anforderungen verwenden.
Funktion für die Umwandlung von Dezimal in Binär:
void Decimal_to_Binary(void)
{
int input =0;
int i;
int count = 0;
int binary [32]; /* 32 Bit, MAXIMUM 32 elements */
printf ("Enter Decimal number to convert into
Binary :");
scanf ("%d", &input);
do
{
i = input%2; /* MOD 2 to get 1 or a 0*/
binary[count] = i; /* Load Elements into the Binary Array */
input = input/2; /* Divide input by 2 to decrement via binary */
count++; /* Count how many elements are needed*/
}while (input > 0);
/* Reverse and output binary digits */
printf ("Binary representation is: ");
do
{
printf ("%d", binary[count - 1]);
count--;
} while (count > 0);
printf ("\n");
}
Funktion für die Umwandlung von Binär in Dezimal:
Die folgende Funktion besteht darin, eine beliebige Binärzahl in die entsprechende Dezimalzahl umzuwandeln:
void Binary_to_Decimal(void)
{
char binaryhold[512];
char *binary;
int i=0;
int dec = 0;
int z;
printf ("Please enter the Binary Digits.\n");
printf ("Binary digits are either 0 or 1 Only ");
printf ("Binary Entry : ");
binary = gets(binaryhold);
i=strlen(binary);
for (z=0; z<i; ++z)
{
dec=dec*2+(binary[z]=='1'? 1:0); /* if Binary[z] is
equal to 1,
then 1 else 0 */
}
printf ("\n");
printf ("Decimal value of %s is %d",
binary, dec);
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 die Regeln brechen, z. B. vergessen, eine Anweisung mit einem Semikolon zu beenden. Wenn Sie das Programm kompilieren, erstellt der Compiler eine Liste aller Syntaxfehler, auf die er stoßen kann.
Ein guter Compiler gibt die Liste mit einer Beschreibung des Fehlers aus und bietet möglicherweise eine mögliche Lösung an. Die Behebung 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 verändert haben, sodass weitere Fehler während der ursprünglichen Kompilierung unterdrückt wurden.
In ähnlicher Weise kann ein einzelner Fehler zu mehreren Fehlern führen. Versuchen Sie, ein Semikolon an das Ende der Hauptfunktion eines Programms zu setzen, das korrekt kompiliert und ausgeführt wird. Wenn Sie es neu kompilieren, erhalten Sie eine riesige Liste von Fehlern, und doch ist es nur ein falsch platziertes Semikolon.
Neben Syntaxfehlern können Compiler auch Warnungen ausgeben. Eine Warnung ist kein Fehler, kann aber Probleme bei der Ausführung Ihres Programms verursachen. Beispielsweise kann die Zuweisung einer Gleitkommazahl mit doppelter Genauigkeit zu einer Gleitkommazahl mit einfacher Genauigkeit zu einem Genauigkeitsverlust führen. Es ist kein Syntaxfehler, könnte 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 einen Float umgewandelt, was alle Compiler-Warnungen beseitigen würde.
x = (float)y;
Logikfehler
Logikfehler treten auf, wenn ein Fehler in der Logik vorliegt. Sie könnten zum Beispiel testen, ob eine Zahl kleiner als 4 und größer als 8 ist. Das kann unmöglich jemals wahr sein, aber wenn es syntaktisch korrekt ist, wird das Programm erfolgreich kompiliert. Betrachten Sie das folgende Beispiel:
if (x < 4 && x > 8)
puts("Will never happen!");
Die Syntax ist korrekt, also wird das Programm kompiliert, aber die Puts-Anweisung wird niemals ausgegeben, da der Wert von x nicht gleichzeitig kleiner als vier und größer als acht sein kann.
Die meisten Logikfehler werden beim anfänglichen Testen des Programms entdeckt. Wenn es sich nicht so verhält, wie Sie es erwartet haben, prüfen Sie die logischen Aussagen genauer und korrigieren sie. Dies gilt nur für offensichtliche logische Fehler. Je größer das Programm, desto mehr Pfade gibt es, desto schwieriger wird es zu überprüfen, ob sich das Programm wie erwartet verhält.
Testen
Im Softwareentwicklungsprozess können Fehler in jeder Phase der Entwicklung eingeschleust werden. Dies liegt daran, dass Verifikationsmethoden früherer Phasen der Softwareentwicklung manuell sind. Daher weist der während der Codierungsaktivität entwickelte Code wahrscheinlich einige Anforderungsfehler und Entwurfsfehler auf, zusätzlich zu Fehlern, die während der Codierungsaktivität eingeführt wurden. Während des Testens 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 ausgeführt wird.
Testen ist also der Prozess der Analyse eines Softwareelements, um den Unterschied zwischen bestehenden und erforderlichen Bedingungen (d. h. Fehlern) zu erkennen und die Funktionen der Softwareelemente zu bewerten. Testen ist also der Prozess der Analyse eines Programms mit der Absicht, Fehler zu finden.
Einige Testprinzipien
- Tests können nicht die Abwesenheit von Fehlern zeigen, sondern nur deren Vorhandensein.
- Je früher ein Fehler gemacht wird, desto teurer ist er.
- Je später ein Fehler entdeckt wird, desto teurer ist er.
Lassen Sie uns nun einige Testtechniken besprechen:
White-Box-Tests
White-Box-Tests sind eine Technik, bei der alle Pfade durch das Programm mit jedem möglichen Wert getestet werden. Dieser Ansatz erfordert einige Kenntnisse 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 korrekt ist, und dann jeden anderen möglichen Wert testen, den eine Ganzzahl annehmen kann, und diesen testen es verhielt sich wie erwartet. In Anbetracht der Anzahl von Datenelementen, die ein typisches Programm haben kann, machen die möglichen Permutationen White-Box-Tests für große Programme extrem schwierig.
White-Box-Tests können auf sicherheitskritische Funktionen eines großen Programms angewendet werden, und ein Großteil des Rests wird mit Black-Box-Tests getestet, wie unten beschrieben. Aufgrund der Anzahl der Permutationen werden White-Box-Tests normalerweise mit einem Testrahmen 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 als strukturelle, Clear- oder Open-Box-Tests bezeichnet.
Black-Box-Tests
Black-Box-Tests ähneln White-Box-Tests, außer dass statt aller möglichen Werte ausgewählte Werte getestet werden. Bei dieser Art von Test kennt der Tester die Eingaben und die erwarteten Ergebnisse, aber nicht unbedingt, wie das Programm zu ihnen gelangt ist. Black-Box-Tests werden manchmal auch als Funktionstests bezeichnet.
Die Testfälle für Black-Box-Tests werden normalerweise entwickelt, sobald die Programmspezifikationen vollständig sind. Die Testfälle basieren auf Äquivalenzklassen.
Äquivalenzklassen
Für jede Eingabe identifiziert eine Äquivalenzklasse die gültigen und ungültigen Zustände. Bei der Definition von Äquivalenzklassen sind im Allgemeinen drei Szenarien einzuplanen.
Wenn die Eingabe einen Bereich oder einen bestimmten Wert angibt, werden ein gültiger Zustand und zwei ungültige Zustände definiert. Wenn beispielsweise eine Zahl zwischen 1 und 20 liegen muss, der gültige Status zwischen 1 und 20 liegt, gibt es einen ungültigen Status für weniger als 1 und einen ungültigen Status über 20.
Wenn die Eingabe einen Bereich oder einen bestimmten Wert ausschließt, werden zwei gültige Zustände und ein ungültiger Zustand definiert. Wenn beispielsweise eine Zahl nicht zwischen 1 und 20 liegen darf, sind die gültigen Zustände kleiner als eins und größer als 20, und der ungültige Zustand liegt 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
Die Grenzwertanalyse berücksichtigt nur die Werte an der Grenze der Eingaben. Wenn beispielsweise eine Zahl zwischen 1 und 20 liegt, können die Testfälle 1, 20, 0 und 21 sein. Der Gedanke dahinter ist, dass, wenn das Programm mit diesen Werten wie erwartet funktioniert, die anderen Werte es auch tun funktionieren wie erwartet.
Die folgende Tabelle gibt einen Überblick über die typischen Grenzen, die Sie möglicherweise identifizieren möchten.
Testing Ranges |
Input type |
Test Values |
Range |
- x[lower_bound]-1
- x[lower_bound]
- x[upper_bound]
- x[upper_bound]+1
|
Boolean |
|
Erstellen eines Testplans
Identifizieren Sie die Äquivalenzklassen und identifizieren Sie für jede Klasse die Grenzen. Nachdem Sie die Grenzen für die Klasse identifiziert haben, schreiben Sie eine Liste mit gültigen und ungültigen Werten an der Grenze und dem erwarteten Verhalten. Der Tester kann dann das Programm mit den Grenzwerten ausführen und angeben, was passiert ist, als der Grenzwert gegen das erforderliche Ergebnis getestet wurde.
Das Folgende könnte ein typischer Testplan sein, der verwendet wird, um zu überprüfen, ob ein Alter eingegeben wird, wobei die akzeptablen Werte im Bereich von 10 bis 110 liegen.
Equivalence Class |
Valid |
Invalid |
Between 10 and 110 |
> 110 |
|
< 10 |
Nachdem wir unsere Äquivalenzklasse definiert haben, können wir nun einen Testplan für das Alter erstellen.
Test Plan |
Value |
State |
Expected Result |
Actual Result |
10 |
Valid |
Continue execution to get name |
|
110 |
Valid |
Continue execution to get name |
|
9 |
Invalid |
Ask for age again |
|
111 |
Invalid |
Ask for age again |
|
Das "Tatsächliche Ergebnis" Spalte bleibt leer, da sie beim Testen ausgefüllt wird. Wenn das Ergebnis wie erwartet ist, wird die Spalte angekreuzt. Wenn nicht, sollte ein Kommentar eingegeben werden, der angibt, was passiert ist.
Seite Geändert am: 08/03/2022