Κεφάλαιο – 5
Εισαγωγή Προγραμματισμού C
Εισαγωγή
Η 'C' είναι μία από τις πιο δημοφιλείς γλώσσες υπολογιστών στον σημερινό κόσμο των υπολογιστών. Η γλώσσα προγραμματισμού 'C' σχεδιάστηκε και αναπτύχθηκε από τους Brian Kernighan και Dennis Ritchie στο The Bell Research Labs το 1972.
Η 'C' είναι μια Γλώσσα που δημιουργήθηκε ειδικά για να επιτρέπει στον προγραμματιστή πρόσβαση σε όλα σχεδόν τα εσωτερικά στοιχεία του μηχανήματος - καταχωρητές, υποδοχές εισόδου/εξόδου και απόλυτες διευθύνσεις. Ταυτόχρονα, το «C» επιτρέπει τον χειρισμό δεδομένων και την προγραμματισμένη σπονδυλοποίηση κειμένου που απαιτείται για να καταστεί δυνατή η οργάνωση πολύπλοκων έργων πολλαπλών προγραμματιστών με οργανωμένο και έγκαιρο τρόπο.
Παρόλο που αυτή η γλώσσα προοριζόταν αρχικά να εκτελείται υπό UNIX, υπήρξε μεγάλο ενδιαφέρον για την εκτέλεσή της στο λειτουργικό σύστημα MS-DOS στον υπολογιστή IBM και σε συμβατά. Είναι μια εξαιρετική γλώσσα για αυτό το περιβάλλον λόγω της απλότητας της έκφρασης, του συμπαγούς κώδικα και του μεγάλου εύρους εφαρμογής.
Επίσης, λόγω της απλότητας και της ευκολίας γραφής ενός μεταγλωττιστή C, είναι συνήθως η πρώτη γλώσσα υψηλού επιπέδου διαθέσιμη σε οποιονδήποτε νέο υπολογιστή, συμπεριλαμβανομένων των μικροϋπολογιστών, των μικρών υπολογιστών και των κεντρικών υπολογιστών.
Γιατί να χρησιμοποιήσετε το C στον προγραμματισμό ανάκτησης δεδομένων
Στον σημερινό κόσμο του προγραμματισμού υπολογιστών, υπάρχουν πολλές διαθέσιμες γλώσσες υψηλού επιπέδου. Αυτές οι γλώσσες είναι καλές με πολλές δυνατότητες κατάλληλες για τις περισσότερες εργασίες προγραμματισμού. Ωστόσο, υπάρχουν αρκετοί λόγοι που το C είναι η πρώτη επιλογή των προγραμματιστών που είναι πρόθυμοι να κάνουν προγραμματισμό για ανάκτηση δεδομένων, προγραμματισμό συστήματος, προγραμματισμό συσκευών ή προγραμματισμό υλικού:
- Η C είναι μια δημοφιλής γλώσσα που προτιμάται από επαγγελματίες προγραμματιστές. Ως αποτέλεσμα, είναι διαθέσιμη μια μεγάλη ποικιλία από μεταγλωττιστές C και χρήσιμα αξεσουάρ.
- Η C είναι μια φορητή γλώσσα. Ένα πρόγραμμα C γραμμένο για ένα σύστημα υπολογιστή μπορεί να μεταγλωττιστεί και να εκτελεστεί σε άλλο σύστημα με ελάχιστη ή καθόλου τροποποίηση. Η φορητότητα ενισχύεται από το πρότυπο ANSI για C, το σύνολο κανόνων για μεταγλωττιστές C.
- Το C επιτρέπει μια ευρεία χρήση λειτουργικών μονάδων στον προγραμματισμό. Ο κώδικας C μπορεί να γραφτεί σε ρουτίνες που ονομάζονται συναρτήσεις. Αυτές οι λειτουργίες μπορούν να επαναχρησιμοποιηθούν σε άλλες εφαρμογές ή προγράμματα. Δεν χρειάζεται να κάνετε επιπλέον προσπάθειες στον προγραμματισμό μιας νέας εφαρμογής για να δημιουργήσετε την ίδια ενότητα που αναπτύξατε σε άλλο προγραμματισμό εφαρμογής στο παρελθόν.
Μπορείτε να χρησιμοποιήσετε αυτήν τη λειτουργία σε νέο πρόγραμμα χωρίς καμία αλλαγή ή κάποιες μικρές αλλαγές. Σε περίπτωση προγραμματισμού ανάκτησης δεδομένων, θα βρείτε αυτή την ποιότητα πολύ χρήσιμη όταν χρειάζεστε να εκτελέσετε τις ίδιες λειτουργίες πολλές φορές σε διαφορετικές εφαρμογές διαφορετικών προγραμμάτων.
- Η C είναι μια ισχυρή και ευέλικτη γλώσσα. Αυτός είναι ο λόγος για τον οποίο η C χρησιμοποιείται για έργα τόσο διαφορετικά όπως λειτουργικά συστήματα, επεξεργαστές κειμένου, γραφικά, υπολογιστικά φύλλα, ακόμη και μεταγλωττιστές για άλλες γλώσσες.
- Η C είναι μια γλώσσα λίγων λέξεων, που περιέχει μόνο μια χούφτα όρους, που ονομάζονται λέξεις-κλειδιά, οι οποίες χρησιμεύουν ως βάση πάνω στην οποία χτίζεται η λειτουργικότητα της γλώσσας. Αυτές οι λέξεις-κλειδιά, που ονομάζονται επίσης δεσμευμένες λέξεις, το κάνουν πιο ισχυρό και δίνουν την ευρεία περιοχή προγραμματισμού και κάνουν έναν προγραμματιστή να αισθάνεται ότι κάνει οποιονδήποτε τύπο προγραμματισμού σε C.
Επιτρέψτε μου να υποθέσω ότι δεν γνωρίζετε τίποτα στο C
Υποθέτω ότι δεν γνωρίζετε τίποτα για τον προγραμματισμό C και επίσης δεν έχετε ιδέα προγραμματισμού. Θα ξεκινήσω με τις πιο βασικές έννοιες του C και θα σας μεταφέρω στο υψηλό επίπεδο του προγραμματισμού C, συμπεριλαμβανομένων των συνήθως εκφοβιστικών εννοιών των δεικτών, των δομών και της δυναμικής κατανομής.
Για να κατανοήσετε πλήρως αυτές τις έννοιες, θα χρειαστεί λίγος χρόνος και δουλειά από την πλευρά σας, επειδή δεν είναι ιδιαίτερα εύκολο να τις κατανοήσετε, αλλά είναι πολύ ισχυρά εργαλεία.
Ο προγραμματισμός σε C είναι ένα τεράστιο πλεονέκτημα σε εκείνους τους τομείς όπου μπορεί να χρειαστεί να χρησιμοποιήσετε τη γλώσσα Assembly, αλλά θα προτιμούσατε να είναι απλό στη σύνταξη και εύκολο στη συντήρηση πρόγραμμα. Ο χρόνος που εξοικονομείται στην κωδικοποίηση του C μπορεί να είναι τεράστιος σε τέτοιες περιπτώσεις.
Παρόλο που η γλώσσα C απολαμβάνει ένα καλό ρεκόρ όταν τα προγράμματα μεταφέρονται από τη μια υλοποίηση στην άλλη, υπάρχουν διαφορές στους μεταγλωττιστές που θα βρείτε κάθε φορά που προσπαθείτε να χρησιμοποιήσετε έναν άλλο μεταγλωττιστή.
Οι περισσότερες διαφορές γίνονται εμφανείς όταν χρησιμοποιείτε μη τυπικές επεκτάσεις, όπως κλήσεις προς το DOS BIOS όταν χρησιμοποιείτε MS-DOS, αλλά ακόμη και αυτές οι διαφορές μπορούν να ελαχιστοποιηθούν με προσεκτική επιλογή δομών προγραμματισμού.
Όταν έγινε φανερό ότι η γλώσσα προγραμματισμού C γινόταν μια πολύ δημοφιλής γλώσσα διαθέσιμη σε ένα ευρύ φάσμα υπολογιστών, μια ομάδα ενδιαφερόμενων ατόμων συναντήθηκε για να προτείνει ένα τυπικό σύνολο κανόνων για τη χρήση της γλώσσας προγραμματισμού C.
Η ομάδα αντιπροσώπευε όλους τους τομείς της βιομηχανίας λογισμικού και μετά από πολλές συναντήσεις και πολλά προκαταρκτικά προσχέδια, έγραψαν τελικά ένα αποδεκτό πρότυπο για τη γλώσσα C. Έγινε αποδεκτό από το Αμερικανικό Εθνικό Ινστιτούτο Προτύπων (ANSI). και από τον Διεθνή Οργανισμό Προτύπων (ISO).
Δεν επιβάλλεται σε καμία ομάδα ή χρήστη, αλλά δεδομένου ότι είναι τόσο ευρέως αποδεκτό, θα ήταν οικονομική αυτοκτονία για κάθε συγγραφέα μεταγλωττιστή να αρνηθεί να συμμορφωθεί με το πρότυπο.
Τα προγράμματα που γράφονται σε αυτό το βιβλίο προορίζονται κυρίως για χρήση σε υπολογιστή IBM-PC ή συμβατό υπολογιστή, αλλά μπορούν να χρησιμοποιηθούν με οποιονδήποτε τυπικό μεταγλωττιστή ANSI, δεδομένου ότι συμμορφώνεται τόσο πολύ με το πρότυπο ANSI.
Ας ξεκινήσουμε
Για να μπορέσετε να κάνετε οτιδήποτε σε οποιαδήποτε γλώσσα και να ξεκινήσετε τον προγραμματισμό, πρέπει να γνωρίζετε πώς να ονομάσετε ένα αναγνωριστικό. Ένα αναγνωριστικό χρησιμοποιείται για οποιαδήποτε μεταβλητή, συνάρτηση, ορισμό δεδομένων κ.λπ. Στη γλώσσα προγραμματισμού C, ένα αναγνωριστικό είναι ένας συνδυασμός αλφαριθμητικών χαρακτήρων, ο πρώτος είναι ένα γράμμα του αλφαβήτου ή μια υπογράμμιση και το υπόλοιπο είναι οποιοδήποτε γράμμα του αλφάβητο, οποιοδήποτε αριθμητικό ψηφίο ή την υπογράμμιση.
Πρέπει να έχετε υπόψη δύο κανόνες κατά την ονομασία αναγνωριστικών.
- Η περίπτωση των αλφαβητικών χαρακτήρων είναι σημαντική. Η C είναι μια γλώσσα με διάκριση πεζών-κεφαλαίων. Αυτό σημαίνει ότι η Ανάκτηση είναι διαφορετική από την ανάκτηση και η Επαναφορά είναι διαφορετική από τα δύο που αναφέρθηκαν προηγουμένως.
- Σύμφωνα με το πρότυπο ANSI-C, μπορούν να χρησιμοποιηθούν τουλάχιστον 31 σημαντικοί χαρακτήρες και θα θεωρηθούν σημαντικοί από έναν συμμορφούμενο μεταγλωττιστή ANSI-C. Εάν χρησιμοποιούνται περισσότεροι από 31, όλοι οι χαρακτήρες πέρα από τον 31ο μπορεί να αγνοηθούν από οποιονδήποτε μεταγλωττιστή.
Λέξεις-κλειδιά
Υπάρχουν 32 λέξεις που ορίζονται ως λέξεις-κλειδιά στο C. Αυτές έχουν προκαθορισμένες χρήσεις και δεν μπορούν να χρησιμοποιηθούν για κανέναν άλλο σκοπό σε ένα πρόγραμμα C. Χρησιμοποιούνται από τον μεταγλωττιστή ως βοήθημα για τη μεταγλώττιση του προγράμματος. Γράφονται πάντα με πεζά. Ακολουθεί πλήρης λίστα:
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 |
Εδώ βλέπουμε τη μαγεία του C. Η υπέροχη συλλογή με μόνο 32 λέξεις-κλειδιά δίνει ευρεία χρήση σε διαφορετικές εφαρμογές. Κάθε πρόγραμμα υπολογιστή έχει δύο οντότητες που πρέπει να ληφθούν υπόψη, τα δεδομένα και το πρόγραμμα. Εξαρτώνται σε μεγάλο βαθμό το ένα από το άλλο και ο προσεκτικός σχεδιασμός και των δύο οδηγεί σε ένα καλά σχεδιασμένο και καλογραμμένο πρόγραμμα.
Ας ξεκινήσουμε με ένα απλό πρόγραμμα C:
/* Πρώτο πρόγραμμα για εκμάθηση C */
#include <stdio.h>
void main()
{
printf("This is a C program\n"); // printing a message
}
Αν και το πρόγραμμα είναι πολύ απλό, αξίζει να σημειωθούν μερικά σημεία. Ας εξετάσουμε το παραπάνω πρόγραμμα. Ό,τι υπάρχει μέσα στο /* και στο */ θεωρείται σχόλιο και θα αγνοηθεί από τον μεταγλωττιστή. Δεν πρέπει να συμπεριλάβετε σχόλια σε άλλα σχόλια, επομένως κάτι τέτοιο δεν επιτρέπεται:
/* αυτό είναι ένα /* σχόλιο */ μέσα σε ένα σχόλιο, το οποίο είναι λάθος */
Υπάρχει επίσης ένας τρόπος τεκμηρίωσης που λειτουργεί μέσα σε μια γραμμή. Χρησιμοποιώντας το // μπορούμε να προσθέσουμε μικρή τεκμηρίωση σε αυτή τη γραμμή.
Κάθε πρόγραμμα C περιέχει μια συνάρτηση που ονομάζεται main. Αυτό είναι το σημείο εκκίνησης του προγράμματος. Κάθε συνάρτηση πρέπει να επιστρέφει μια τιμή. Σε αυτό το πρόγραμμα η συνάρτηση main δεν επιστρέφει τιμή επιστροφής επομένως έχουμε γράψει void main. Θα μπορούσαμε επίσης να γράψουμε αυτό το πρόγραμμα ως:
/* Πρώτο πρόγραμμα για εκμάθηση C */
#include <stdio.h>
main()
{
printf("This is a C program\n"); // printing a message
return 0;
}
Και τα δύο προγράμματα είναι ίδια και εκτελούν την ίδια εργασία. Το αποτέλεσμα και των δύο προγραμμάτων θα εκτυπώσει την ακόλουθη έξοδο στην οθόνη:
Αυτό είναι ένα πρόγραμμα C
Το
#include<stdio.h> επιτρέπει στο πρόγραμμα να αλληλεπιδρά με την οθόνη, το πληκτρολόγιο και το σύστημα αρχείων του υπολογιστή σας. Θα το βρείτε στην αρχή σχεδόν κάθε προγράμματος C.
Το
main() δηλώνει την έναρξη της συνάρτησης, ενώ οι δύο αγκύλες δείχνουν την αρχή και το τέλος της συνάρτησης. Οι σγουρές αγκύλες στο C χρησιμοποιούνται για την ομαδοποίηση εντολών όπως σε μια συνάρτηση ή στο σώμα ενός βρόχου. Μια τέτοια ομαδοποίηση είναι γνωστή ως σύνθετη πρόταση ή μπλοκ.
Το
printf("Αυτό είναι πρόγραμμα C\n"); εκτυπώνει τις λέξεις στην οθόνη. Το προς εκτύπωση κείμενο περικλείεται σε διπλά εισαγωγικά. Το \n στο τέλος του κειμένου λέει στο πρόγραμμα να εκτυπώσει μια νέα γραμμή ως μέρος της εξόδου. Η συνάρτηση printf() χρησιμοποιείται για την εμφάνιση της εξόδου στην οθόνη.
Τα περισσότερα προγράμματα C είναι με πεζά γράμματα. Συνήθως θα βρείτε κεφαλαία γράμματα που χρησιμοποιούνται σε ορισμούς προεπεξεργαστή που θα συζητηθούν αργότερα ή μέσα σε εισαγωγικά ως μέρη συμβολοσειρών χαρακτήρων.
Σύνταξη του προγράμματος
Αφήστε το όνομα του προγράμματός μας να είναι CPROG.C. Για να εισαγάγετε και να μεταγλωττίσετε το πρόγραμμα C, ακολουθήστε τα εξής βήματα:
- Δημιουργήστε τον ενεργό κατάλογο των προγραμμάτων σας C και ξεκινήστε τον επεξεργαστή σας. Για αυτό μπορεί να χρησιμοποιηθεί οποιοσδήποτε επεξεργαστής κειμένου, αλλά οι περισσότεροι μεταγλωττιστές C, όπως το Borland's Turbo C++, διαθέτουν ένα ενσωματωμένο περιβάλλον ανάπτυξης (IDE) που σας επιτρέπει να εισάγετε, να μεταγλωττίσετε και να συνδέσετε τα προγράμματά σας σε μια βολική ρύθμιση.
- Γράψτε και αποθηκεύστε τον πηγαίο κώδικα. Θα πρέπει να ονομάσετε το αρχείο CPROG.C.
- Μεταγλώττιση και σύνδεση CPROG.C. Εκτελέστε την κατάλληλη εντολή που καθορίζεται από τα εγχειρίδια του μεταγλωττιστή σας. Θα πρέπει να λάβετε ένα μήνυμα που να δηλώνει ότι δεν υπήρχαν σφάλματα ή προειδοποιήσεις.
- Ελέγξτε τα μηνύματα του μεταγλωττιστή. Εάν δεν λάβετε σφάλματα ή προειδοποιήσεις, όλα θα πρέπει να είναι εντάξει. Εάν υπάρχει κάποιο σφάλμα κατά την πληκτρολόγηση του προγράμματος, ο μεταγλωττιστής θα το πιάσει και θα εμφανίσει ένα μήνυμα σφάλματος. Διορθώστε το σφάλμα, που εμφανίζεται στο μήνυμα σφάλματος.
- Το πρώτο σας πρόγραμμα C πρέπει τώρα να έχει μεταγλωττιστεί και να είναι έτοιμο για εκτέλεση. Εάν εμφανίσετε μια λίστα καταλόγου με όλα τα αρχεία με το όνομα CPROG, θα λάβετε τα τέσσερα αρχεία με διαφορετική επέκταση που περιγράφονται ως εξής:
- CPROG.C, το αρχείο πηγαίου κώδικα
- CPROG.BAK, το αντίγραφο ασφαλείας του αρχείου πηγής που δημιουργήσατε με το πρόγραμμα επεξεργασίας
- CPROG.OBJ, περιέχει τον κωδικό αντικειμένου για CPROG.C
- CPROG.EXE, το εκτελέσιμο πρόγραμμα που δημιουργήθηκε κατά τη μεταγλώττιση και σύνδεση του CPROG.C
- Για να εκτελέσετε ή να εκτελέσετε το CPROG.EXE, απλώς πληκτρολογήστε cprog. Στην οθόνη εμφανίζεται το μήνυμα This is a C program.
Τώρα ας εξετάσουμε το ακόλουθο πρόγραμμα:
/* Πρώτο πρόγραμμα για εκμάθηση C */ // 1
// 2
#include <stdio.h> // 3
// 4
main() // 5
{
// 6
printf("This is a C program\n"); // 7
// 8
return 0; // 9
} // 10
Όταν κάνετε μεταγλώττιση αυτού του προγράμματος, ο μεταγλωττιστής εμφανίζει ένα μήνυμα παρόμοιο με το ακόλουθο:
cprog.c(8) : Σφάλμα: `;' αναμένεται
ας σπάσουμε αυτό το μήνυμα σφάλματος σε μέρη. cprog.c είναι το όνομα του αρχείου όπου βρέθηκε το σφάλμα. Το (8) είναι ο αριθμός γραμμής όπου βρέθηκε το σφάλμα. Σφάλμα: `;' αναμένεται είναι Μια περιγραφή του σφάλματος.
Αυτό το μήνυμα είναι αρκετά κατατοπιστικό και σας λέει ότι στη γραμμή 8 του CPROG.C ο μεταγλωττιστής περίμενε να βρει ένα ερωτηματικό αλλά δεν το έκανε. Ωστόσο, γνωρίζετε ότι το ερωτηματικό παραλείφθηκε από τη γραμμή 7, επομένως υπάρχει μια απόκλιση.
Γιατί ο μεταγλωττιστής αναφέρει ένα σφάλμα στη γραμμή 8 όταν, στην πραγματικότητα, ένα ερωτηματικό παραλείφθηκε από τη γραμμή 7. Η απάντηση βρίσκεται στο γεγονός ότι η C δεν ενδιαφέρεται για πράγματα όπως οι διακοπές μεταξύ των γραμμών. Το ερωτηματικό που ανήκει μετά την πρόταση printf() θα μπορούσε να είχε τοποθετηθεί στην επόμενη γραμμή, αν και θα ήταν κακός προγραμματισμός στην πράξη.
Μόνο αφού συναντήσει την επόμενη εντολή (return) στη γραμμή 8, ο μεταγλωττιστής είναι σίγουρος ότι λείπει το ερωτηματικό. Επομένως, ο μεταγλωττιστής αναφέρει ότι το σφάλμα βρίσκεται στη γραμμή 8.
Μπορεί να υπάρχουν πολλές πιθανότητες διαφορετικών τύπων σφαλμάτων. Ας συζητήσουμε τη σύνδεση των μηνυμάτων σφάλματος. Τα σφάλματα σύνδεσης είναι σχετικά σπάνια και συνήθως προκύπτουν από ορθογραφικό λάθος του ονόματος μιας συνάρτησης βιβλιοθήκης C. Σε αυτήν την περίπτωση, λαμβάνετε ένα μήνυμα Σφάλμα: ακαθόριστα σύμβολα: μήνυμα σφάλματος, ακολουθούμενο από το ανορθόγραφο όνομα. Μόλις διορθώσετε την ορθογραφία, το πρόβλημα θα πρέπει να εξαφανιστεί.
Εκτύπωση αριθμών
Ας δούμε το ακόλουθο παράδειγμα:
// Πώς να εκτυπώσετε τους αριθμούς //
#include<stdio.h>
void main()
{
int num = 10;
printf(“ The Number Is %d”, num);
}
Η έξοδος του προγράμματος θα εμφανιστεί στην οθόνη ως εξής:
Ο αριθμός είναι 10
Το σύμβολο % χρησιμοποιείται για να σηματοδοτήσει την έξοδο πολλών διαφορετικών τύπων μεταβλητών. Ο χαρακτήρας που ακολουθεί το σύμβολο % είναι ένα d, το οποίο σηματοδοτεί τη ρουτίνα εξόδου για να πάρει μια δεκαδική τιμή και να την εξάγει.
Χρήση μεταβλητών
Στο C, μια μεταβλητή πρέπει να δηλωθεί για να μπορέσει να χρησιμοποιηθεί. Οι μεταβλητές μπορούν να δηλωθούν στην αρχή οποιουδήποτε μπλοκ κώδικα, αλλά οι περισσότερες βρίσκονται στην αρχή κάθε συνάρτησης. Οι περισσότερες τοπικές μεταβλητές δημιουργούνται όταν καλείται η συνάρτηση και καταστρέφονται κατά την επιστροφή από αυτήν τη συνάρτηση.
Για να χρησιμοποιήσετε μεταβλητές στα προγράμματά σας C, πρέπει να γνωρίζετε τους ακόλουθους κανόνες όταν δίνετε το όνομα σε μεταβλητές στο C:
- Το όνομα μπορεί να περιέχει γράμματα, ψηφία και τον χαρακτήρα υπογράμμισης (_).
- Ο πρώτος χαρακτήρας του ονόματος πρέπει να είναι γράμμα. Η υπογράμμιση είναι επίσης νομικός πρώτος χαρακτήρας, αλλά δεν συνιστάται η χρήση του.
- Το C κάνει διάκριση πεζών-κεφαλαίων, επομένως το όνομα της μεταβλητής num είναι Διαφορετικό από το Num.
- Οι λέξεις-κλειδιά C δεν μπορούν να χρησιμοποιηθούν ως ονόματα μεταβλητών. Μια λέξη-κλειδί είναι μια λέξη που αποτελεί μέρος της γλώσσας C.
Η ακόλουθη λίστα περιέχει μερικά παραδείγματα νόμιμων και παράνομων ονομάτων μεταβλητών C:
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 |
Το πρώτο νέο πράγμα που ξεχωρίζει είναι η πρώτη γραμμή του σώματος του main():
int num = 10;
Αυτή η γραμμή ορίζει μια μεταβλητή με το όνομα 'num' τύπου int και την αρχικοποιεί με την τιμή 10. Αυτό μπορεί επίσης να έχει γραφτεί ως:
int num; /* define uninitialized variable 'num' */
/* and after all variable definitions: */
num = 10; /* assigns value 10 to variable 'num' */
Οι μεταβλητές μπορεί να ορίζονται στην αρχή ενός μπλοκ (μεταξύ των αγκύλων {και}), συνήθως αυτό είναι στην αρχή ενός σώματος συνάρτησης, αλλά μπορεί επίσης να είναι στην αρχή ενός άλλου τύπου μπλοκ.
Μεταβλητές που ορίζονται στην αρχή ενός μπλοκ από προεπιλογή στην κατάσταση 'auto'. Αυτό σημαίνει ότι υπάρχουν μόνο κατά την εκτέλεση του μπλοκ. Όταν ξεκινήσει η εκτέλεση της συνάρτησης, οι μεταβλητές θα δημιουργηθούν αλλά το περιεχόμενό τους θα είναι απροσδιόριστο. Όταν επιστρέψει η συνάρτηση, οι μεταβλητές θα καταστραφούν. Ο ορισμός θα μπορούσε επίσης να γραφτεί ως:
auto int num = 10;
Εφόσον ο ορισμός με ή χωρίς την αυτόματη λέξη-κλειδί είναι εντελώς ισοδύναμος, η αυτόματη λέξη-κλειδί είναι προφανώς μάλλον περιττή.
Ωστόσο, μερικές φορές αυτό δεν είναι αυτό που θέλετε. Ας υποθέσουμε ότι θέλετε μια συνάρτηση να μετράει πόσες φορές καλείται. Εάν η μεταβλητή καταστρέφεται κάθε φορά που επιστρέφει η συνάρτηση, αυτό δεν θα είναι δυνατό.
Επομένως, είναι δυνατό να δώσουμε στη μεταβλητή αυτό που ονομάζεται στατική διάρκεια, που σημαίνει ότι θα παραμείνει ανέπαφη καθ' όλη τη διάρκεια της εκτέλεσης του προγράμματος. Για παράδειγμα:
static int num = 10;
Αυτό αρχικοποιεί τη μεταβλητή num σε 10 στην αρχή της εκτέλεσης του προγράμματος. Από εκεί και πέρα η τιμή θα παραμείνει ανέγγιχτη. η μεταβλητή δεν θα αρχικοποιηθεί ξανά εάν η συνάρτηση κληθεί πολλές φορές.
Μερικές φορές δεν αρκεί η πρόσβαση στη μεταβλητή από μία μόνο συνάρτηση ή μπορεί να μην είναι βολικό να μεταβιβαστεί η τιμή μέσω μιας παραμέτρου σε όλες τις άλλες συναρτήσεις που τη χρειάζονται.
Αλλά εάν χρειάζεστε πρόσβαση στη μεταβλητή από όλες τις συναρτήσεις σε ολόκληρο το αρχείο προέλευσης, αυτό μπορεί επίσης να γίνει με τη στατική λέξη-κλειδί, αλλά βάζοντας τον ορισμό εκτός όλων των συναρτήσεων. Για παράδειγμα:
#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;
}
Και υπάρχουν επίσης περιπτώσεις όπου μια μεταβλητή πρέπει να είναι προσβάσιμη από ολόκληρο το πρόγραμμα, το οποίο μπορεί να αποτελείται από πολλά αρχεία προέλευσης. Αυτό ονομάζεται καθολική μεταβλητή και πρέπει να αποφεύγεται όταν δεν απαιτείται.
Αυτό γίνεται επίσης βάζοντας τον ορισμό εκτός όλων των συναρτήσεων, αλλά χωρίς τη χρήση της στατικής λέξης-κλειδιού:
#include <stdio.h>
int num = 10; /* will be accessible from entire program! */
int main(void)
{
printf("The Number Is: %d\n", num);
return 0;
}
Υπάρχει επίσης η εξωτερική λέξη-κλειδί, η οποία χρησιμοποιείται για την πρόσβαση σε καθολικές μεταβλητές σε άλλες ενότητες. Υπάρχουν επίσης μερικοί προσδιορισμοί που μπορείτε να προσθέσετε σε ορισμούς μεταβλητών. Το σημαντικότερο από αυτά είναι ο κ.στ. Μια μεταβλητή που ορίζεται ως const δεν μπορεί να τροποποιηθεί.
Υπάρχουν δύο ακόμη τροποποιητές που χρησιμοποιούνται λιγότερο συχνά. Ο τροποποιητής πτητικού και καταχωρητή. Ο πτητικός τροποποιητής απαιτεί από τον μεταγλωττιστή να έχει πραγματικά πρόσβαση στη μεταβλητή κάθε φορά που διαβάζεται. Μπορεί να μην βελτιστοποιήσει τη μεταβλητή τοποθετώντας την σε έναν καταχωρητή ή κάτι τέτοιο. Αυτό χρησιμοποιείται κυρίως για σκοπούς επεξεργασίας πολλαπλών νημάτων και διακοπής κ.λπ.
Ο τροποποιητής καταχωρητή ζητά από τον μεταγλωττιστή να βελτιστοποιήσει τη μεταβλητή σε έναν καταχωρητή. Αυτό είναι δυνατό μόνο με τις αυτόματες μεταβλητές και σε πολλές περιπτώσεις ο μεταγλωττιστής μπορεί να επιλέξει καλύτερα τις μεταβλητές για βελτιστοποίηση σε καταχωρητές, επομένως αυτή η λέξη-κλειδί είναι παρωχημένη. Η μόνη άμεση συνέπεια της δημιουργίας ενός καταχωρητή μεταβλητής είναι ότι δεν μπορεί να ληφθεί η διεύθυνσή του.
Ο πίνακας μεταβλητών, που δίνεται στην επόμενη σελίδα, περιγράφει την κλάση αποθήκευσης πέντε τύπων κλάσεων αποθήκευσης.
Στον πίνακα βλέπουμε ότι η λέξη-κλειδί extern τοποθετείται σε δύο σειρές. Η εξωτερική λέξη-κλειδί χρησιμοποιείται σε συναρτήσεις για να δηλώσει μια στατική εξωτερική μεταβλητή που ορίζεται αλλού.
Αριθμητικοί τύποι μεταβλητών
Το C παρέχει πολλούς διαφορετικούς τύπους αριθμητικών μεταβλητών επειδή διαφορετικές αριθμητικές τιμές έχουν διαφορετικές απαιτήσεις αποθήκευσης μνήμης. Αυτοί οι αριθμητικοί τύποι διαφέρουν ως προς την ευκολία με την οποία μπορούν να εκτελεστούν ορισμένες μαθηματικές πράξεις σε αυτούς.
Οι μικροί ακέραιοι αριθμοί απαιτούν λιγότερη μνήμη για αποθήκευση και ο υπολογιστής σας μπορεί να εκτελέσει μαθηματικές πράξεις με τέτοιους αριθμούς πολύ γρήγορα. Οι μεγάλοι ακέραιοι αριθμοί και οι τιμές κινητής υποδιαστολής απαιτούν περισσότερο χώρο αποθήκευσης και περισσότερο χρόνο για μαθηματικές πράξεις. Χρησιμοποιώντας τους κατάλληλους τύπους μεταβλητών, διασφαλίζετε ότι το πρόγραμμά σας εκτελείται όσο το δυνατόν πιο αποτελεσματικά.
Οι αριθμητικές μεταβλητές του C εμπίπτουν στις ακόλουθες δύο κύριες κατηγορίες:
- Ακέραιες μεταβλητές
- Μεταβλητές κινητής υποδιαστολής
Σε καθεμία από αυτές τις κατηγορίες υπάρχουν δύο ή περισσότεροι συγκεκριμένοι τύποι μεταβλητών. Ο πίνακας που δίνεται στη συνέχεια δείχνει την ποσότητα της μνήμης, σε byte, που απαιτείται για τη διατήρηση μιας μεμονωμένης μεταβλητής κάθε τύπου.
Ο τύπος char μπορεί να είναι ισοδύναμος είτε με υπογεγραμμένο είτε με ανυπόγραφο χαρακτήρα, αλλά είναι πάντα ξεχωριστός τύπος από οποιοδήποτε από αυτά.
Στο C δεν υπάρχει διαφορά μεταξύ της αποθήκευσης χαρακτήρων ή των αντίστοιχων αριθμητικών τους τιμών σε μια μεταβλητή, επομένως δεν χρειάζεται επίσης να μετατραπεί μια συνάρτηση μεταξύ ενός χαρακτήρα και της αριθμητικής του τιμής ή το αντίστροφο. Για τους άλλους ακέραιους τύπους, εάν παραλείψετε υπογεγραμμένο ή ανυπόγραφο θα υπογραφεί η προεπιλογή, έτσι π.χ. το int και το signed int είναι ισοδύναμα.
Ο τύπος int πρέπει να είναι μεγαλύτερος ή ίσος με τον τύπο short και μικρότερος ή ίσος με τον τύπο long. Εάν χρειάζεται απλώς να αποθηκεύσετε κάποιες τιμές που δεν είναι εξαιρετικά μεγάλες, είναι συχνά καλή ιδέα να χρησιμοποιήσετε τον τύπο int. είναι συνήθως το μέγεθος που μπορεί να αντιμετωπίσει ο επεξεργαστής το πιο εύκολο και επομένως το πιο γρήγορο.
Με πολλούς μεταγλωττιστές το double και το long double είναι ισοδύναμα. Αυτό σε συνδυασμό με το γεγονός ότι οι περισσότερες τυπικές μαθηματικές συναρτήσεις λειτουργούν με τον τύπο διπλό, είναι ένας καλός λόγος για να χρησιμοποιείτε πάντα τον τύπο διπλό εάν πρέπει να εργαστείτε με κλασματικούς αριθμούς.
Ο παρακάτω πίνακας είναι για να περιγράψει καλύτερα τους τύπους μεταβλητών:
Τύποι ειδικού σκοπού που χρησιμοποιούνται συνήθως:
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 |
Για να κατανοήσουμε καλύτερα αυτές τις μεταβλητές ας πάρουμε ένα παράδειγμα:
/* Πρόγραμμα για να πει το εύρος και το μέγεθος σε byte της μεταβλητής C */
#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;
}
Το αποτέλεσμα του προγράμματος μετά την εκτέλεση θα εμφανίζεται ως:
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 |
Πριν από τη χρήση της, μια μεταβλητή σε ένα πρόγραμμα C, πρέπει να δηλωθεί. Μια δήλωση μεταβλητής λέει στον μεταγλωττιστή το όνομα και τον τύπο μιας μεταβλητής και προαιρετικά αρχικοποιεί τη μεταβλητή σε μια συγκεκριμένη τιμή.
Εάν το πρόγραμμά σας επιχειρήσει να χρησιμοποιήσει μια μεταβλητή που δεν έχει δηλωθεί, ο μεταγλωττιστής δημιουργεί ένα μήνυμα σφάλματος. Μια δήλωση μεταβλητής έχει την ακόλουθη μορφή:
typename varname;
typename καθορίζει τον τύπο της μεταβλητής και πρέπει να είναι μία από τις λέξεις-κλειδιά. varname είναι το όνομα της μεταβλητής. Μπορείτε να δηλώσετε πολλές μεταβλητές του ίδιου τύπου σε μία γραμμή διαχωρίζοντας τα ονόματα των μεταβλητών με κόμματα:
int count, number, start; /* three integer variables */
float percent, total; /* two float variables */
Η λέξη-κλειδί typedef
Η λέξη-κλειδί typedef χρησιμοποιείται για τη δημιουργία νέου ονόματος για έναν υπάρχοντα τύπο δεδομένων. Στην πραγματικότητα, το typedef δημιουργεί ένα συνώνυμο. Για παράδειγμα, η δήλωση
typedef int ακέραιος;
εδώ βλέπουμε το typedef να δημιουργεί ακέραιο ως συνώνυμο του int. Στη συνέχεια, μπορείτε να χρησιμοποιήσετε ακέραιο για να ορίσετε μεταβλητές τύπου int, όπως σε αυτό το παράδειγμα:
ακέραιος αριθμός;
Έτσι, το typedef δεν δημιουργεί νέο τύπο δεδομένων, σας επιτρέπει μόνο να χρησιμοποιήσετε ένα διαφορετικό όνομα για έναν προκαθορισμένο τύπο δεδομένων.
Αρχικοποίηση αριθμητικών μεταβλητών
Όταν δηλώνεται οποιαδήποτε μεταβλητή, ο μεταγλωττιστής λαμβάνει εντολή να αφήσει χώρο αποθήκευσης για τη μεταβλητή. Ωστόσο, η τιμή που είναι αποθηκευμένη σε αυτό το διάστημα, η τιμή της μεταβλητής, δεν ορίζεται. Μπορεί να είναι μηδέν ή μπορεί να είναι κάποια τυχαία "σκουπίδια"; αξία. Πριν χρησιμοποιήσετε μια μεταβλητή, θα πρέπει πάντα να την αρχικοποιείτε σε μια γνωστή τιμή. Ας πάρουμε αυτό το παράδειγμα:
int count; /* Set aside storage space for count */
count = 0; /* Store 0 in count */
Αυτή η δήλωση χρησιμοποιεί το σύμβολο ίσου (=), που είναι ο τελεστής εκχώρησης του C. Μπορείτε επίσης να αρχικοποιήσετε μια μεταβλητή όταν δηλωθεί. Για να το κάνετε αυτό, ακολουθήστε το όνομα της μεταβλητής στη δήλωση δήλωσης με σύμβολο ίσου και την επιθυμητή αρχική τιμή:
int count = 0;
double rate = 0.01, complexity = 28.5;
Προσέξτε να μην αρχικοποιήσετε μια μεταβλητή με τιμή εκτός του επιτρεπόμενου εύρους. Ακολουθούν δύο παραδείγματα αρχικοποιήσεων εκτός εύρους:
int amount = 100000;
unsigned int length = -2500;
Ο μεταγλωττιστής C δεν εντοπίζει τέτοια σφάλματα. Το πρόγραμμά σας μπορεί να μεταγλωττιστεί και να συνδεθεί, αλλά μπορεί να λάβετε απροσδόκητα αποτελέσματα όταν εκτελείται το πρόγραμμα.
Ας πάρουμε το ακόλουθο παράδειγμα για να υπολογίσουμε τον συνολικό αριθμό τομέων σε έναν δίσκο:
// Πρόγραμμα μοντέλου για τον υπολογισμό τομέων σε έναν δίσκο //
#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();
}
Η έξοδος του προγράμματος είναι η εξής:
Enter The No. of Cylinders in the Disk
1024
Total Number of Sectors in the disk = 16386048
Σε αυτό το παράδειγμα βλέπουμε τρία νέα πράγματα να μάθουμε. Το #define χρησιμοποιείται για τη χρήση συμβολικών σταθερών στο πρόγραμμα ή σε ορισμένες περιπτώσεις για εξοικονόμηση χρόνου ορίζοντας μεγάλες λέξεις σε μικρά σύμβολα.
Εδώ έχουμε ορίσει τον αριθμό των τομέων ανά πλευρά που είναι 63 ως SECTOR_PER_SIDE για να γίνει το πρόγραμμα εύκολο στην κατανόηση. Η ίδια περίπτωση ισχύει για το #define SIDE_PER_CYLINDER 254. Η scanf() χρησιμοποιείται για τη λήψη της εισόδου από τον χρήστη.
Εδώ λαμβάνουμε τον αριθμό των κυλίνδρων ως είσοδο από τον χρήστη. * χρησιμοποιείται για τον πολλαπλασιασμό δύο ή περισσότερων τιμών όπως φαίνεται στο παράδειγμα.
Η συνάρτηση getch() λαμβάνει βασικά μια είσοδο χαρακτήρων από το πληκτρολόγιο. Πληκτρολογώντας getch(); εδώ σταματάμε την οθόνη μέχρι να πατηθεί οποιοδήποτε πλήκτρο από το πληκτρολόγιο.
Χειριστές
Ένας τελεστής είναι ένα σύμβολο που δίνει εντολή στο C να εκτελέσει κάποια λειτουργία ή ενέργεια σε έναν ή περισσότερους τελεστές. Ο τελεστής είναι κάτι στο οποίο ενεργεί ένας τελεστής. Στο C, όλοι οι τελεστές είναι εκφράσεις. Οι τελεστές C χωρίζονται στις ακόλουθες τέσσερις κατηγορίες:
- Ο τελεστής ανάθεσης
- Μαθηματικοί τελεστές
- Σχετικοί τελεστές
- Λογικοί τελεστές
Χειριστής ανάθεσης
Ο τελεστής εκχώρησης είναι το σύμβολο ίσου (=). Η χρήση του ίσου στον προγραμματισμό είναι διαφορετική από τη χρήση του σε κανονικές μαθηματικές αλγεβρικές σχέσεις. Αν γράψετε
x = y;
Σε ένα πρόγραμμα C, δεν σημαίνει ότι το "x είναι ίσο με y". Αντίθετα, σημαίνει "εκχωρήστε την τιμή του y στο x". Σε μια δήλωση εκχώρησης C, η δεξιά πλευρά μπορεί να είναι οποιαδήποτε έκφραση και η αριστερή πλευρά πρέπει να είναι ένα όνομα μεταβλητής. Έτσι, η φόρμα έχει ως εξής:
μεταβλητή = έκφραση;
Κατά την εκτέλεση, η έκφραση αξιολογείται και η τιμή που προκύπτει εκχωρείται στη μεταβλητή.
Μαθηματικοί τελεστές
Οι μαθηματικοί τελεστές του C εκτελούν μαθηματικές πράξεις όπως πρόσθεση και αφαίρεση. Το C έχει δύο μονομερείς μαθηματικούς τελεστές και πέντε δυαδικούς μαθηματικούς τελεστές. Οι μοναδικοί μαθηματικοί τελεστές ονομάζονται έτσι επειδή παίρνουν έναν μόνο τελεστή. Το C έχει δύο ενιαίους μαθηματικούς τελεστές.
Οι τελεστές αύξησης και μείωσης μπορούν να χρησιμοποιηθούν μόνο με μεταβλητές, όχι με σταθερές. Η λειτουργία που εκτελείται είναι να προσθέσουμε ένα ή να αφαιρέσουμε ένα από τον τελεστή. Με άλλα λόγια, οι δηλώσεις ++x; και --y; είναι τα ισοδύναμα αυτών των δηλώσεων:
x = x + 1;
y = y - 1;
Οι δυαδικοί μαθηματικοί τελεστές παίρνουν δύο τελεστές. Οι πρώτοι τέσσερις δυαδικοί τελεστές, οι οποίοι περιλαμβάνουν τις κοινές μαθηματικές πράξεις που βρίσκονται σε μια αριθμομηχανή (+, -, *, /), είναι γνωστοί σε εσάς. Ο πέμπτος τελεστής Modulus επιστρέφει το υπόλοιπο όταν ο πρώτος τελεστής διαιρεθεί με τον δεύτερο τελεστή. Για παράδειγμα, 11 συντελεστής 4 ισούται με 3 (το 11 διαιρείται με το 4, δύο φορές και το 3 έχει περισσέψει).
Σχέσιοι χειριστές
Οι σχεσιακές τελεστές του C χρησιμοποιούνται για τη σύγκριση παραστάσεων. Μια έκφραση που περιέχει έναν σχεσιακό τελεστή αξιολογείται είτε ως true (1) είτε ως false (0). Το C έχει έξι σχεσιακούς τελεστές.
Λογικοί τελεστές
Οι λογικοί τελεστές του C σάς επιτρέπουν να συνδυάσετε δύο ή περισσότερες σχεσιακές εκφράσεις σε μια μοναδική παράσταση που αξιολογείται είτε ως true είτε ως false. Οι λογικοί τελεστές αξιολογούν είτε true είτε false, ανάλογα με την true ή false τιμή των τελεστών τους.
Αν το x είναι μια ακέραια μεταβλητή, οι εκφράσεις που χρησιμοποιούν λογικούς τελεστές θα μπορούσαν να γραφτούν με τους εξής τρόπους:
(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 |
Πράγματα που πρέπει να θυμάστε σχετικά με τις λογικές εκφράσεις
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 |
Ο χειριστής των κομμάτων
Το κόμμα χρησιμοποιείται συχνά στο C ως απλό σημείο στίξης, για να διαχωρίσει δηλώσεις μεταβλητών, ορίσματα συναρτήσεων κ.λπ. Σε ορισμένες περιπτώσεις, το κόμμα λειτουργεί ως τελεστής.
Μπορείτε να σχηματίσετε μια έκφραση διαχωρίζοντας δύο δευτερεύουσες εκφράσεις με κόμμα. Το αποτέλεσμα είναι το εξής:
- Και οι δύο εκφράσεις αξιολογούνται, με την αριστερή έκφραση να αξιολογείται πρώτη.
- Ολόκληρη η έκφραση αξιολογείται στην τιμή της σωστής έκφρασης.
Για παράδειγμα,η ακόλουθη πρόταση εκχωρεί την τιμή του b στο x, μετά αυξάνει το a και μετά αυξάνει το b:
x = (a++, b++);
Προτεραιότητα τελεστή C (Σύνοψη τελεστών C)
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. |
|
Ας πάρουμε ένα παράδειγμα χρήσης τελεστών:
/* Χρήση τελεστών */
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;
}
και το αποτέλεσμα αυτού του προγράμματος θα εμφανιστεί στην οθόνη ως:
x=3, y=1, z=1
a=2.000000, b=3.141590, c=2.000000
Κάτι περισσότερο για τα printf() και Scanf()
Εξετάστε τις ακόλουθες δύο προτάσεις printf
printf(“\t %d\n”, num);
printf(“%5.2f”, fract);
Στην πρώτη πρόταση printf \t ζητά τη μετατόπιση της καρτέλας στην οθόνη, το όρισμα %d λέει στον μεταγλωττιστή ότι η τιμή του num πρέπει να εκτυπωθεί ως δεκαδικός ακέραιος. \n προκαλεί την έναρξη της νέας εξόδου από νέα γραμμή.
Στη δεύτερη πρόταση printf %5.2f λέει στον μεταγλωττιστή ότι η έξοδος πρέπει να είναι σε κινητή υποδιαστολή, με πέντε θέσεις σε όλες και δύο θέσεις στα δεξιά της υποδιαστολής. Περισσότερα για τον χαρακτήρα ανάστροφης κάθετο παρουσιάζονται στον παρακάτω πίνακα:
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 |
Ας εξετάσουμε την ακόλουθη δήλωση scanf
scanf(“%d”, &num);
Τα δεδομένα από το πληκτρολόγιο λαμβάνονται με τη λειτουργία σάρωσης. Στην παραπάνω μορφή, το & Το σύμβολο (συμπερασματικό) πριν από κάθε όνομα μεταβλητής είναι ένας τελεστής που καθορίζει τη διεύθυνση του ονόματος της μεταβλητής.
Κάνοντας αυτό, η εκτέλεση σταματά και περιμένει να πληκτρολογηθεί η τιμή της μεταβλητής num. Όταν εισαχθεί η ακέραια τιμή και πατηθεί το κλειδί επιστροφής, ο υπολογιστής προχωρά στην επόμενη δήλωση. Οι κωδικοί μορφής scanf και printf παρατίθενται στον ακόλουθο πίνακα:
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 |
Δηλώσεις ελέγχου
Ένα πρόγραμμα αποτελείται από έναν αριθμό εντολών που συνήθως εκτελούνται με τη σειρά. Τα προγράμματα μπορεί να είναι πολύ πιο ισχυρά εάν μπορούμε να ελέγξουμε τη σειρά με την οποία εκτελούνται οι δηλώσεις.
Οι δηλώσεις χωρίζονται σε τρεις γενικούς τύπους:
- Εκχώρηση, όπου οι τιμές, συνήθως τα αποτελέσματα των υπολογισμών, αποθηκεύονται σε μεταβλητές.
- Είσοδος / Έξοδος, τα δεδομένα διαβάζονται ή εκτυπώνονται.
- Έλεγχος, το πρόγραμμα αποφασίζει για το τι θα κάνει στη συνέχεια.
Αυτή η ενότητα θα συζητήσει τη χρήση εντολών ελέγχου στο C. Θα δείξουμε πώς μπορούν να χρησιμοποιηθούν για τη σύνταξη ισχυρών προγραμμάτων από:
- Επανάληψη σημαντικών ενοτήτων του προγράμματος.
- Επιλογή μεταξύ προαιρετικών ενοτήτων ενός προγράμματος.
Η δήλωση αν αλλιώς
Χρησιμοποιείται για να αποφασίσετε εάν θα κάνετε κάτι σε ένα ειδικό σημείο ή για να αποφασίσετε μεταξύ δύο τρόπων δράσης.
Το παρακάτω τεστ αποφασίζει εάν ένας μαθητής έχει περάσει εξετάσεις με βαθμολογία επιτυχίας 45
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");
Κάθε έκδοση αποτελείται από μια δοκιμή, στην πρόταση που βρίσκεται σε αγκύλες μετά το if. Εάν το τεστ είναι αληθές τότε τηρείται η επόμενη πρόταση. Εάν είναι ψευδής, τότε η δήλωση που ακολουθεί την άλλη τηρείται εάν υπάρχει. Μετά από αυτό, το υπόλοιπο πρόγραμμα συνεχίζεται κανονικά.
Εάν επιθυμούμε να έχουμε περισσότερες από μία εντολές μετά το αν ή το άλλο, θα πρέπει να ομαδοποιηθούν μεταξύ των αγκύλων. Μια τέτοια ομαδοποίηση ονομάζεται σύνθετη πρόταση ή μπλοκ.
if (result >= 45)
{ printf("Passed\n");
printf("Congratulations\n");
}
else
{ printf("Failed\n");
printf("Better Luck Next Time\n");
}
Μερικές φορές επιθυμούμε να πάρουμε μια πολύπλευρη απόφαση με βάση πολλές προϋποθέσεις. Ο πιο γενικός τρόπος για να γίνει αυτό είναι χρησιμοποιώντας την παραλλαγή else if στη δήλωση if.
Αυτό λειτουργεί με καταρράκτη αρκετές συγκρίσεις. Μόλις ένα από αυτά δώσει ένα αληθινό αποτέλεσμα, εκτελείται η ακόλουθη πρόταση ή μπλοκ και δεν πραγματοποιούνται περαιτέρω συγκρίσεις. Στο παρακάτω παράδειγμα απονέμουμε βαθμούς ανάλογα με το αποτέλεσμα της εξέτασης.
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");
Σε αυτό το παράδειγμα, όλες οι συγκρίσεις δοκιμάζουν μια μεμονωμένη μεταβλητή που ονομάζεται αποτέλεσμα. Σε άλλες περιπτώσεις, κάθε δοκιμή μπορεί να περιλαμβάνει διαφορετική μεταβλητή ή κάποιο συνδυασμό δοκιμών. Το ίδιο μοτίβο μπορεί να χρησιμοποιηθεί με περισσότερα ή λιγότερα άλλα αν και το τελικό μόνο άλλο μπορεί να μείνει έξω.
Εναπόκειται στον προγραμματιστή να επινοήσει τη σωστή δομή για κάθε πρόβλημα προγραμματισμού. Για να κατανοήσουμε καλύτερα τη χρήση του if else ας δούμε το παράδειγμα
#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;
}
Αποτέλεσμα του προγράμματος
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
Η δήλωση διακόπτη
Αυτή είναι μια άλλη μορφή απόφασης πολλαπλών κατευθύνσεων. Είναι καλά δομημένο, αλλά μπορεί να χρησιμοποιηθεί μόνο σε ορισμένες περιπτώσεις όπου:
- Μόνο μία μεταβλητή ελέγχεται, όλοι οι κλάδοι πρέπει να εξαρτώνται από την τιμή αυτής της μεταβλητής. Η μεταβλητή πρέπει να είναι ακέραιος τύπος. (int, long, short ή char).
- Κάθε πιθανή τιμή της μεταβλητής μπορεί να ελέγχει έναν κλάδο. Ένας τελικός προεπιλεγμένος κλάδος catch all μπορεί προαιρετικά να χρησιμοποιηθεί για την παγίδευση όλων των απροσδιόριστων περιπτώσεων.
Το παράδειγμα που δίνεται παρακάτω θα ξεκαθαρίσει τα πράγματα. Αυτή είναι μια συνάρτηση που μετατρέπει έναν ακέραιο σε μια ασαφή περιγραφή. Είναι χρήσιμο όταν μας απασχολεί μόνο η μέτρηση μιας ποσότητας όταν είναι αρκετά μικρή.
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;
}
}
Κάθε ενδιαφέρουσα περίπτωση παρατίθεται με μια αντίστοιχη ενέργεια. Η εντολή break αποτρέπει την εκτέλεση οποιωνδήποτε περαιτέρω εντολών αφήνοντας το διακόπτη. Εφόσον η περίπτωση 3 και η περίπτωση 4 δεν έχουν επόμενο διάλειμμα, συνεχίζουν να επιτρέπουν την ίδια ενέργεια για πολλές τιμές αριθμού.
Και οι δύο κατασκευές if και switch επιτρέπουν στον προγραμματιστή να κάνει μια επιλογή από έναν αριθμό πιθανών ενεργειών. Ας δούμε ένα παράδειγμα:
#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;
}
Η έξοδος του προγράμματος θα είναι
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
The break Statement
Έχουμε ήδη συναντήσει break στη συζήτηση της δήλωσης switch. Χρησιμοποιείται για έξοδο από έναν βρόχο ή έναν διακόπτη, ο έλεγχος περνά στην πρώτη πρόταση πέρα από τον βρόχο ή έναν διακόπτη.
Με τους βρόχους, το break μπορεί να χρησιμοποιηθεί για να εξαναγκάσει μια πρόωρη έξοδο από τον βρόχο ή για να εφαρμόσει έναν βρόχο με μια δοκιμή για έξοδο στη μέση του σώματος του βρόχου. Μια διακοπή εντός ενός βρόχου θα πρέπει πάντα να προστατεύεται μέσα σε μια δήλωση if που παρέχει τη δοκιμή για τον έλεγχο της συνθήκης εξόδου.
Η δήλωση συνέχειας
Αυτό είναι παρόμοιο με το break, αλλά συναντάται λιγότερο συχνά. Λειτουργεί μόνο σε βρόχους όπου το αποτέλεσμα είναι να εξαναγκάσει ένα άμεσο άλμα στη δήλωση ελέγχου βρόχου.
- Σε ένα βρόχο while, μεταβείτε στη δήλωση δοκιμής.
- Σε έναν βρόχο do while, μεταβείτε στη δήλωση δοκιμής.
- Σε έναν βρόχο for, μεταβείτε στη δοκιμή και εκτελέστε την επανάληψη.
Όπως ένα διάλειμμα, η συνέχεια θα πρέπει να προστατεύεται από μια δήλωση if. Είναι απίθανο να το χρησιμοποιείτε πολύ συχνά. Για να κατανοήσουμε καλύτερα τη χρήση του διαλείμματος και να συνεχίσουμε, ας εξετάσουμε το ακόλουθο πρόγραμμα:
#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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
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
Βρόχοι
Ο άλλος κύριος τύπος δήλωσης ελέγχου είναι ο βρόχος. Οι βρόχοι επιτρέπουν την επανάληψη μιας δήλωσης ή μπλοκ εντολών. Οι υπολογιστές είναι πολύ καλοί στο να επαναλαμβάνουν απλές εργασίες πολλές φορές. Ο βρόχος είναι ο τρόπος του C για να το πετύχει αυτό.
Το
C σάς δίνει τη δυνατότητα επιλογής τριών τύπων βρόχου, while, do-while και for.
- Ο βρόχος while συνεχίζει να επαναλαμβάνει μια ενέργεια έως ότου μια συσχετισμένη δοκιμή επιστρέψει false. Αυτό είναι χρήσιμο όταν ο προγραμματιστής δεν γνωρίζει εκ των προτέρων πόσες φορές θα διασχιστεί ο βρόχος.
- Οι βρόχοι do while είναι παρόμοιοι, αλλά η δοκιμή πραγματοποιείται μετά την εκτέλεση του σώματος του βρόχου. Αυτό διασφαλίζει ότι το σώμα του βρόχου εκτελείται τουλάχιστον μία φορά.
- Ο βρόχος for χρησιμοποιείται συχνά, συνήθως όπου ο βρόχος διασχίζεται σταθερό αριθμό φορών. Είναι πολύ ευέλικτο και οι αρχάριοι προγραμματιστές θα πρέπει να φροντίζουν να μην κάνουν κατάχρηση της ισχύος που προσφέρει.
Το while Loop
Ο βρόχος while επαναλαμβάνει μια πρόταση έως ότου η δοκιμή στην κορυφή αποδειχθεί ψευδής. Για παράδειγμα, εδώ είναι μια συνάρτηση για την επιστροφή του μήκους μιας συμβολοσειράς. Θυμηθείτε ότι η συμβολοσειρά αναπαρίσταται ως ένας πίνακας χαρακτήρων που τερματίζεται από έναν μηδενικό χαρακτήρα '\0'.
int string_length(char string[])
{ int i = 0;
while (string[i] != '\0')
i++;
return(i);
}
Η συμβολοσειρά μεταβιβάζεται στη συνάρτηση ως όρισμα. Το μέγεθος του πίνακα δεν έχει καθοριστεί, η συνάρτηση θα λειτουργήσει για μια συμβολοσειρά οποιουδήποτε μεγέθους.
Ο βρόχος while χρησιμοποιείται για την εξέταση των χαρακτήρων στη συμβολοσειρά έναν κάθε φορά μέχρι να βρεθεί ο μηδενικός χαρακτήρας. Στη συνέχεια, ο βρόχος βγαίνει και επιστρέφεται το ευρετήριο του null.
Ενώ ο χαρακτήρας δεν είναι μηδενικός, ο δείκτης αυξάνεται και η δοκιμή επαναλαμβάνεται. Θα προχωρήσουμε σε βάθος τους πίνακες αργότερα. Ας δούμε ένα παράδειγμα για τον βρόχο while:
#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;
}
και το αποτέλεσμα εμφανίζεται ως εξής:
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
Το do while Loop
Αυτό είναι πολύ παρόμοιο με τον βρόχο while εκτός από το ότι η δοκιμή πραγματοποιείται στο τέλος του σώματος του βρόχου. Αυτό εγγυάται ότι ο βρόχος εκτελείται τουλάχιστον μία φορά πριν συνεχιστεί.
Μια τέτοια ρύθμιση χρησιμοποιείται συχνά όταν πρόκειται να διαβαστούν δεδομένα. Στη συνέχεια, το τεστ επαληθεύει τα δεδομένα και επανέρχεται για να διαβάσει ξανά εάν ήταν απαράδεκτο.
do
{
printf("Enter 1 for yes, 0 for no :");
scanf("%d", &input_value);
} while (input_value != 1 && input_value != 0)
Για να κατανοήσουμε καλύτερα τον βρόχο do while, ας δούμε το ακόλουθο παράδειγμα:
#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;
}
Το αποτέλεσμα του προγράμματος εμφανίζεται ως εξής:
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
Το for Loop
Ο βρόχος for λειτουργεί καλά όταν ο αριθμός των επαναλήψεων του βρόχου είναι γνωστός πριν από την εισαγωγή του βρόχου. Η κεφαλή του βρόχου αποτελείται από τρία μέρη που χωρίζονται με ερωτηματικά.
- Η πρώτη εκτελείται πριν από την εισαγωγή του βρόχου. Αυτή είναι συνήθως η προετοιμασία της μεταβλητής βρόχου.
- Το δεύτερο είναι μια δοκιμή, ο βρόχος βγαίνει όταν αυτό επιστρέψει false.
- Η τρίτη είναι μια πρόταση που εκτελείται κάθε φορά που ολοκληρώνεται το σώμα του βρόχου. Αυτό είναι συνήθως μια αύξηση του μετρητή βρόχου.
Το παράδειγμα είναι μια συνάρτηση που υπολογίζει τον μέσο όρο των αριθμών που είναι αποθηκευμένοι σε έναν πίνακα. Η συνάρτηση παίρνει τον πίνακα και τον αριθμό των στοιχείων ως ορίσματα.
float average(float array[], int count)
{
float total = 0.0;
int i;
for(i = 0; i < count; i++)
total += array[i];
return(total / count);
}
Ο βρόχος for διασφαλίζει ότι προστίθεται ο σωστός αριθμός στοιχείων πίνακα πριν από τον υπολογισμό του μέσου όρου.
Οι τρεις προτάσεις στην κορυφή ενός βρόχου for συνήθως κάνουν μόνο ένα πράγμα η καθεμία, ωστόσο οποιαδήποτε από αυτές μπορεί να μείνει κενή. Μια κενή πρώτη ή τελευταία πρόταση θα σημαίνει ότι δεν υπάρχει αρχικοποίηση ή προσαύξηση που εκτελείται. Μια κενή πρόταση σύγκρισης θα αντιμετωπίζεται πάντα ως αληθής. Αυτό θα κάνει τον βρόχο να λειτουργεί επ' αόριστον, εκτός εάν διακοπεί με κάποιο άλλο μέσο. Αυτό μπορεί να είναι μια δήλωση επιστροφής ή διακοπής.
Είναι επίσης δυνατό να συμπιέσετε πολλές δηλώσεις στην πρώτη ή την τρίτη θέση, χωρίζοντάς τις με κόμματα. Αυτό επιτρέπει έναν βρόχο με περισσότερες από μία μεταβλητές ελέγχου. Το παρακάτω παράδειγμα επεξηγεί τον ορισμό ενός τέτοιου βρόχου, με τις μεταβλητές hi και lo να ξεκινούν από το 100 και το 0 αντίστοιχα και να συγκλίνουν.
Ο βρόχος for δίνει μια ποικιλία συντομογραφιών που θα χρησιμοποιηθούν σε αυτόν. Προσέξτε την ακόλουθη έκφραση, σε αυτήν την έκφραση ο μεμονωμένος βρόχος περιέχει δύο βρόχους for σε αυτόν. Εδώ το hi-- είναι ίδιο με το hi = hi - 1 και το lo++ είναι το ίδιο με το lo = lo + 1,
for(hi = 100, lo = 0; hi >= lo; hi--, lo++)
Ο βρόχος for είναι εξαιρετικά ευέλικτος και επιτρέπει τον καθορισμό πολλών τύπων συμπεριφοράς προγράμματος απλά και γρήγορα. Ας δούμε ένα παράδειγμα του βρόχου for
#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;
}
Το αποτέλεσμα του προγράμματος εμφανίζεται ως εξής:
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
Η δήλωση goto
Το C έχει μια δήλωση goto που επιτρέπει την πραγματοποίηση μη δομημένων πηδημάτων. Για να χρησιμοποιήσετε μια δήλωση goto, απλώς χρησιμοποιείτε τη δεσμευμένη λέξη goto ακολουθούμενη από το συμβολικό όνομα στο οποίο θέλετε να μεταβείτε. Στη συνέχεια, το όνομα τοποθετείται οπουδήποτε στο πρόγραμμα ακολουθούμενο από άνω και κάτω τελεία. Μπορείτε να πηδήξετε σχεδόν οπουδήποτε μέσα σε μια συνάρτηση, αλλά δεν επιτρέπεται να μεταπηδήσετε σε έναν βρόχο, αν και σας επιτρέπεται να πηδήξετε έξω από έναν βρόχο.
Αυτό το συγκεκριμένο πρόγραμμα είναι πραγματικά ένα χάος, αλλά είναι ένα καλό παράδειγμα του γιατί οι συγγραφείς λογισμικού προσπαθούν να εξαλείψουν τη χρήση της δήλωσης goto όσο το δυνατόν περισσότερο. Το μόνο μέρος σε αυτό το πρόγραμμα όπου είναι λογικό να χρησιμοποιηθεί το goto είναι, όπου το πρόγραμμα πηδά από τους τρεις ένθετους βρόχους σε ένα άλμα. Σε αυτήν την περίπτωση, θα ήταν μάλλον ακατάστατο να ρυθμίσετε μια μεταβλητή και να πηδήξετε διαδοχικά από κάθε έναν από τους τρεις ένθετους βρόχους, αλλά μια δήλωση goto σας βγάζει και από τις τρεις με πολύ συνοπτικό τρόπο.
Μερικά άτομα λένε ότι η δήλωση goto δεν πρέπει ποτέ να χρησιμοποιείται σε καμία περίπτωση, αλλά αυτό είναι στενόμυαλη σκέψη. Εάν υπάρχει ένα μέρος όπου ένα goto θα κάνει σαφώς πιο καθαρή ροή ελέγχου από κάποια άλλη κατασκευή, μη διστάσετε να το χρησιμοποιήσετε, ωστόσο, όπως είναι στο υπόλοιπο πρόγραμμα στην οθόνη σας. Ας δούμε το παράδειγμα:
#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;
}
Ας δούμε τα αποτελέσματα που εμφανίζονται
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.
Δείκτες
Μερικές φορές θέλουμε να μάθουμε πού βρίσκεται μια μεταβλητή στη μνήμη. Ένας δείκτης περιέχει τη διεύθυνση μιας μεταβλητής που έχει μια συγκεκριμένη τιμή. Κατά τη δήλωση ενός δείκτη, ένας αστερίσκος τοποθετείται αμέσως πριν από το όνομα του δείκτη. .
Η διεύθυνση της θέσης μνήμης όπου είναι αποθηκευμένη η μεταβλητή μπορεί να βρεθεί τοποθετώντας ένα συμπλεκτικό σύμβολο μπροστά από το όνομα της μεταβλητής.
int num; /* Normal integer variable */
int *numPtr; /* Pointer to an integer variable */
Το ακόλουθο παράδειγμα εκτυπώνει την τιμή της μεταβλητής και τη διεύθυνση στη μνήμη αυτής της μεταβλητής.
printf("The value %d is stored at address %X\n", num, &num);
Για να εκχωρήσετε τη διεύθυνση της μεταβλητής num στον δείκτη numPtr, εκχωρείτε τη διεύθυνση της μεταβλητής num, όπως στο παράδειγμα που δίνεται παρακάτω:
numPtr = #
Για να μάθετε τι είναι αποθηκευμένο στη διεύθυνση που υποδεικνύεται από το numPtr, η μεταβλητή πρέπει να αποκατασταθεί. Η αποαναφορά επιτυγχάνεται με τον αστερίσκο με τον οποίο δηλώθηκε ο δείκτης.
printf("The value %d is stored at address %X\n", *numPtr, numPtr);
Όλες οι μεταβλητές σε ένα πρόγραμμα βρίσκονται στη μνήμη. Οι δηλώσεις που δίνονται παρακάτω ζητούν από τον μεταγλωττιστή να δεσμεύσει 4 byte μνήμης σε έναν υπολογιστή 32 bit για τη μεταβλητή κινητής υποδιαστολής x και στη συνέχεια να βάλει την τιμή 6,5 σε αυτήν.
float x;
x = 6.5;
Καθώς η θέση διεύθυνσης στη μνήμη οποιασδήποτε μεταβλητής λαμβάνεται με την τοποθέτηση του τελεστή & πριν από το όνομά του επομένως &x είναι η διεύθυνση του x. Το C μας επιτρέπει να πάμε ένα στάδιο παραπέρα και να ορίσουμε μια μεταβλητή, που ονομάζεται δείκτης που περιέχει τη διεύθυνση άλλων μεταβλητών. Μάλλον μπορούμε να πούμε ότι ο δείκτης δείχνει σε άλλη μεταβλητή. Για παράδειγμα:
float x;
float* px;
x = 6.5;
px = &x;
ορίζει το px ως δείκτη σε αντικείμενα τύπου float και τον ορίζει ίσο με τη διεύθυνση του x. Έτσι, το *px αναφέρεται στην τιμή του x:
Ας εξετάσουμε τις ακόλουθες δηλώσεις:
int var_x;
int* ptrX;
var_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("value of x : %d", var_x);
Η πρώτη γραμμή κάνει τον μεταγλωττιστή να κρατήσει ένα κενό στη μνήμη για έναν ακέραιο. Η δεύτερη γραμμή λέει στον μεταγλωττιστή να κρατήσει χώρο για να αποθηκεύσει έναν δείκτη.
Ο δείκτης είναι μια θέση αποθήκευσης για μια διεύθυνση. Η τρίτη γραμμή θα πρέπει να σας υπενθυμίζει τις δηλώσεις scanf. Η διεύθυνση "&" Ο χειριστής λέει στον μεταγλωττιστή να πάει στο μέρος που αποθήκευσε το var_x και μετά να δώσει τη διεύθυνση της θέσης αποθήκευσης στο ptrX.
Ο αστερίσκος * μπροστά από μια μεταβλητή λέει στον μεταγλωττιστή να αποαναφέρει τον δείκτη και να μεταβεί στη μνήμη. Στη συνέχεια, μπορείτε να κάνετε αναθέσεις σε μεταβλητή που είναι αποθηκευμένη σε αυτήν τη θέση. Μπορείτε να αναφέρετε μια μεταβλητή και να έχετε πρόσβαση στα δεδομένα της μέσω ενός δείκτη. Ας δούμε ένα παράδειγμα δεικτών:
/* απεικόνιση της χρήσης του δείκτη */
#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;
}
Η έξοδος του προγράμματος θα εμφανιστεί ως εξής:
The value is 39 39 39
The value is 13 13 13
Ας δούμε ένα άλλο παράδειγμα για να κατανοήσουμε καλύτερα τη χρήση των δεικτών:
#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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
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
Πίνακες
Ένας πίνακας είναι μια συλλογή μεταβλητών του ίδιου τύπου. Τα μεμονωμένα στοιχεία πίνακα προσδιορίζονται από έναν ακέραιο ευρετήριο. Στο C ο δείκτης ξεκινά από το μηδέν και γράφεται πάντα μέσα σε αγκύλες.
Έχουμε ήδη συναντήσει πίνακες μονής διάστασης που δηλώνονται με αυτόν τον τρόπο
int αποτελέσματα[20];
Οι πίνακες μπορούν να έχουν περισσότερες διαστάσεις, οπότε μπορεί να δηλωθούν ως
int results_2d[20][5];
int results_3d[20][5][3];
Κάθε ευρετήριο έχει το δικό του σύνολο αγκύλων. Ένας πίνακας δηλώνεται στην κύρια συνάρτηση, συνήθως περιλαμβάνει λεπτομέρειες διαστάσεων. Είναι δυνατό να χρησιμοποιηθεί ένας άλλος τύπος που ονομάζεται δείκτης στη θέση ενός πίνακα. Αυτό σημαίνει ότι οι διαστάσεις δεν καθορίζονται αμέσως, αλλά μπορεί να εκχωρηθεί χώρος όπως απαιτείται. Αυτή είναι μια προηγμένη τεχνική που απαιτείται μόνο σε ορισμένα εξειδικευμένα προγράμματα.
Για παράδειγμα, εδώ είναι μια απλή συνάρτηση για την άθροιση όλων των ακεραίων αριθμών σε έναν πίνακα μονής διάστασης.
int add_array(int array[], int size)
{
int i;
int total = 0;
for(i = 0; i < size; i++)
total += array[i];
return(total);
}
Το πρόγραμμα που δίνεται στη συνέχεια θα δημιουργήσει μια συμβολοσειρά, θα αποκτήσει πρόσβαση σε ορισμένα δεδομένα σε αυτήν, θα την εκτυπώσει. Αποκτήστε ξανά πρόσβαση χρησιμοποιώντας δείκτες και, στη συνέχεια, εκτυπώστε τη συμβολοσειρά. Θα πρέπει να τυπώσει "Hi!" και "012345678" σε διαφορετικές γραμμές. Ας δούμε την κωδικοποίηση του προγράμματος:
#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);
}
Τα [] (τετράγωνα στηρίγματα) χρησιμοποιούνται για τη δήλωση του πίνακα. Η γραμμή του προγράμματος char Str [STR_LENGTH]; δηλώνει έναν πίνακα δέκα χαρακτήρων. Αυτοί είναι δέκα μεμονωμένοι χαρακτήρες, οι οποίοι είναι όλοι μαζί στη μνήμη στο ίδιο μέρος. Είναι δυνατή η πρόσβαση σε όλα μέσω του ονόματος της μεταβλητής μας Str μαζί με ένα [n] όπου n είναι ο αριθμός του στοιχείου.
Όταν μιλάμε για πίνακα θα πρέπει πάντα να λαμβάνετε υπόψη ότι όταν ο C δηλώνει έναν πίνακα δέκα, τα στοιχεία στα οποία μπορείτε να έχετε πρόσβαση αριθμούνται από το 0 έως το 9. Η πρόσβαση στο πρώτο στοιχείο αντιστοιχεί στην πρόσβαση στο 0ο στοιχείο. Έτσι, στην περίπτωση των Arrays να μετράτε πάντα από το 0 έως το μέγεθος του πίνακα - 1.
Επόμενη ειδοποίηση ότι βάζουμε τα γράμματα "Γεια!" στον πίνακα, αλλά μετά βάζουμε ένα '\0' που πιθανώς αναρωτιέστε τι είναι αυτό. "\0" σημαίνει NULL και αντιπροσωπεύει το τέλος της συμβολοσειράς. Όλες οι συμβολοσειρές χαρακτήρων πρέπει να τελειώνουν με αυτόν τον ειδικό χαρακτήρα '\0'. Εάν δεν το κάνουν, και στη συνέχεια κάποιος καλέσει το printf στη συμβολοσειρά, τότε το printf θα ξεκινήσει από τη θέση μνήμης της συμβολοσειράς σας και θα συνεχίσει να εκτυπώνει πείτε του ότι συναντά '\0' και έτσι θα καταλήξετε με ένα σωρό σκουπίδια στο τέλος της χορδής σου. Φροντίστε λοιπόν να τερματίσετε τις χορδές σας σωστά.
Πίνακες χαρακτήρων
Μια σταθερά συμβολοσειράς , όπως
"Είμαι συμβολοσειρά"
είναι ένας πίνακας χαρακτήρων. Αντιπροσωπεύεται εσωτερικά στο C από τους χαρακτήρες ASCII στη συμβολοσειρά, π.χ., "I", κενό, "a", "m",…ή την παραπάνω συμβολοσειρά, και τερματίζεται με τον ειδικό μηδενικό χαρακτήρα "\0", ώστε τα προγράμματα να μπορούν βρείτε το τέλος της συμβολοσειράς.
Οι σταθερές συμβολοσειράς χρησιμοποιούνται συχνά για να γίνει κατανοητή η έξοδος του κώδικα χρησιμοποιώντας το printf:
printf("Hello, world\n");
printf("The value of a is: %f\n", a);
Οι σταθερές συμβολοσειράς μπορούν να συσχετιστούν με μεταβλητές. Το C παρέχει τη μεταβλητή τύπου χαρακτήρα, η οποία μπορεί να περιέχει έναν χαρακτήρα (1 byte) τη φορά. Μια συμβολοσειρά χαρακτήρων αποθηκεύεται σε έναν πίνακα τύπου χαρακτήρων, έναν χαρακτήρα ASCII ανά τοποθεσία.
Μην ξεχνάτε ποτέ ότι, καθώς οι συμβολοσειρές τερματίζονται συμβατικά από τον μηδενικό χαρακτήρα "\0", χρειαζόμαστε μία επιπλέον θέση αποθήκευσης στον πίνακα.
Το C δεν παρέχει κανέναν τελεστή που χειρίζεται ολόκληρες συμβολοσειρές ταυτόχρονα. Οι συμβολοσειρές χειρίζονται είτε μέσω δεικτών είτε μέσω ειδικών ρουτίνες που είναι διαθέσιμες από την τυπική συμβολοσειρά βιβλιοθήκης συμβολοσειρών.
Η χρήση δεικτών χαρακτήρων είναι σχετικά εύκολη αφού το όνομα ενός πίνακα είναι απλώς ένας δείκτης στο πρώτο του στοιχείο. Εξετάστε το πρόγραμμα που δίνεται στη συνέχεια:
#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);
}
Η έξοδος του προγράμματος θα είναι η εξής:
Αρχικό μήνυμα: Γεια, είμαι χορδή. τι είσαι?
Κείμενο_1: Γεια σας, είμαι χορδή. τι είσαι?
Κείμενο_2: Γεια σας, είμαι χορδή. τι είσαι;
Η τυπική βιβλιοθήκη "string" περιέχει πολλές χρήσιμες λειτουργίες για τον χειρισμό συμβολοσειρών, τις οποίες θα μάθουμε στην ενότητα συμβολοσειράς αργότερα.
Πρόσβαση στα Στοιχεία
Για να αποκτήσετε πρόσβαση σε ένα μεμονωμένο στοιχείο στον πίνακα, ο αριθμός ευρετηρίου ακολουθεί το όνομα της μεταβλητής σε αγκύλες. Στη συνέχεια, η μεταβλητή μπορεί να αντιμετωπιστεί όπως οποιαδήποτε άλλη μεταβλητή στο C. Το ακόλουθο παράδειγμα εκχωρεί μια τιμή στο πρώτο στοιχείο του πίνακα.
x[0] = 16;
Το ακόλουθο παράδειγμα εκτυπώνει την τιμή του τρίτου στοιχείου σε έναν πίνακα.
printf("%d\n", x[2]);
Το παρακάτω παράδειγμα χρησιμοποιεί τη συνάρτηση scanf για να διαβάσει μια τιμή από το πληκτρολόγιο στο τελευταίο στοιχείο ενός πίνακα με δέκα στοιχεία.
scanf("%d", &x[9]);
Αρχικοποίηση στοιχείων πίνακα
Οι πίνακες μπορούν να αρχικοποιηθούν όπως οποιεσδήποτε άλλες μεταβλητές με ανάθεση. Καθώς ένας πίνακας περιέχει περισσότερες από μία τιμές, οι μεμονωμένες τιμές τοποθετούνται σε σγουρές αγκύλες και διαχωρίζονται με κόμματα. Το παρακάτω παράδειγμα προετοιμάζει έναν δεκαδιάστατο πίνακα με τις πρώτες δέκα τιμές του πίνακα τριών χρόνων.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Αυτό αποθηκεύει την ανάθεση των τιμών μεμονωμένα όπως στο παρακάτω παράδειγμα.
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;
Κάνοντας βρόχο μέσα από έναν πίνακα
Καθώς ο πίνακας ευρετηριάζεται διαδοχικά, μπορούμε να χρησιμοποιήσουμε τον βρόχο for για να εμφανίσουμε όλες τις τιμές ενός πίνακα. Το παρακάτω παράδειγμα εμφανίζει όλες τις τιμές ενός πίνακα:
#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;
}
Αν και η έξοδος θα εκτυπώνει τις διαφορετικές τιμές κάθε φορά, το αποτέλεσμα θα εμφανίζεται κάπως έτσι:
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
Πολυδιάστατοι πίνακες
Ένας πίνακας μπορεί να έχει περισσότερες από μία διαστάσεις. Επιτρέποντας στον πίνακα να έχει περισσότερες από μία διαστάσεις παρέχει μεγαλύτερη ευελιξία. Για παράδειγμα, τα υπολογιστικά φύλλα είναι χτισμένα σε έναν δισδιάστατο πίνακα. έναν πίνακα για τις σειρές και έναν πίνακα για τις στήλες.
Το ακόλουθο παράδειγμα χρησιμοποιεί έναν δισδιάστατο πίνακα με δύο σειρές, καθεμία από τις οποίες περιέχει πέντε στήλες:
#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;
}
Η έξοδος αυτού του προγράμματος θα εμφανιστεί ως εξής:
1 2 3 4 5
2 4 6 8 10
Χορδές
Μια συμβολοσειρά είναι μια ομάδα χαρακτήρων, συνήθως γράμματα του αλφαβήτου. Για να μορφοποιήσετε την οθόνη εκτύπωσης με τέτοιο τρόπο ώστε να φαίνεται ωραία, να έχει ουσιαστικά ονόματα και τίτλους και να είναι αισθητικά ευχάριστη για εσάς και τα άτομα που χρησιμοποιούν το έξοδο του προγράμματός σας.
Στην πραγματικότητα, έχετε ήδη χρησιμοποιήσει συμβολοσειρές στα παραδείγματα των προηγούμενων θεμάτων. Αλλά δεν είναι η πλήρης εισαγωγή των χορδών. Υπάρχουν πολλές πιθανές περιπτώσεις στον προγραμματισμό, όπου η χρήση μορφοποιημένων συμβολοσειρών βοηθά τον προγραμματιστή να αποφύγει τις πάρα πολλές επιπλοκές στο πρόγραμμα και φυσικά πάρα πολλά σφάλματα.
Ένας πλήρης ορισμός μιας συμβολοσειράς είναι μια σειρά δεδομένων τύπου χαρακτήρων που τερματίζονται από έναν μηδενικό χαρακτήρα (‘\0’).
Όταν ο C πρόκειται να χρησιμοποιήσει μια συμβολοσειρά δεδομένων με κάποιο τρόπο, είτε για να τη συγκρίνει με μια άλλη συμβολοσειρά, να την εξάγει, να την αντιγράψει σε άλλη συμβολοσειρά ή οτιδήποτε άλλο, οι συναρτήσεις ρυθμίζονται για να κάνουν αυτό που καλούνται να κάνουν μέχρι να εντοπιστεί ένα μηδενικό στοιχείο.
Δεν υπάρχει βασικός τύπος δεδομένων για μια συμβολοσειρά στο C Αντίθετα. Οι συμβολοσειρές στο C υλοποιούνται ως πίνακας χαρακτήρων. Για παράδειγμα, για να αποθηκεύσετε ένα όνομα, θα μπορούσατε να δηλώσετε έναν πίνακα χαρακτήρων αρκετά μεγάλο ώστε να αποθηκεύει το όνομα και, στη συνέχεια, να χρησιμοποιήσετε τις κατάλληλες συναρτήσεις βιβλιοθήκης για να χειριστείτε το όνομα.
Το ακόλουθο παράδειγμα εμφανίζει τη συμβολοσειρά στην οθόνη, που έχει εισαχθεί από τον χρήστη:
#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;
}
Η εκτέλεση του προγράμματος θα είναι:
Εισαγάγετε το όνομά σας: Tarun Tyagi
Το όνομα που εισαγάγατε ήταν Tarun Tyagi
Μερικές κοινές συναρτήσεις συμβολοσειράς
Η τυπική βιβλιοθήκη string.h περιέχει πολλές χρήσιμες συναρτήσεις για τον χειρισμό συμβολοσειρών. Μερικές από τις πιο χρήσιμες λειτουργίες έχουν παρουσιαστεί εδώ.
Η συνάρτηση strlen
Η συνάρτηση strlen χρησιμοποιείται για τον προσδιορισμό του μήκους μιας συμβολοσειράς. Ας μάθουμε τη χρήση του strlen με παράδειγμα:
#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;
}
Και η εκτέλεση του προγράμματος θα είναι η εξής:
Εισαγάγετε το όνομά σας: Tarun Subhash Tyagi
Το όνομά σας έχει 19 χαρακτήρες
Εισαγάγετε το όνομά σας: Preeti Tarun
Το όνομά σας έχει 12 χαρακτήρες
Η συνάρτηση strcpy
Η συνάρτηση strcpy χρησιμοποιείται για την αντιγραφή μιας συμβολοσειράς σε μια άλλη. Ας μάθουμε τη χρήση αυτής της συνάρτησης με παράδειγμα:
#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;
}
και η έξοδος του προγράμματος θα είναι ως εξής:
Εισαγάγετε την πρώτη συμβολοσειρά: Tarun
Εισαγάγετε τη δεύτερη συμβολοσειρά: Tyagi
πρώτο: Tarun και δεύτερον: Tyagi Πριν από το strcpy()
πρώτο: Tarun και δεύτερον: Tarun Μετά από strcpy()
Η συνάρτηση strcmp
Η συνάρτηση strcmp χρησιμοποιείται για τη σύγκριση δύο συμβολοσειρών μεταξύ τους. Το όνομα της μεταβλητής ενός πίνακα δείχνει τη διεύθυνση βάσης αυτού του πίνακα. Επομένως, εάν προσπαθήσουμε να συγκρίνουμε δύο συμβολοσειρές χρησιμοποιώντας τα ακόλουθα, θα συγκρίνουμε δύο διευθύνσεις, οι οποίες προφανώς δεν θα ήταν ποτέ ίδιες καθώς δεν είναι δυνατό να αποθηκευτούν δύο τιμές στην ίδια θέση.
if (first == second) /* Δεν μπορεί ποτέ να γίνει σύγκριση συμβολοσειρών */
Το παρακάτω παράδειγμα χρησιμοποιεί τη συνάρτηση strcmp για να συγκρίνει δύο συμβολοσειρές:
#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;
}
Και η εκτέλεση του προγράμματος θα είναι η εξής:
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
Η συνάρτηση strcat
Η συνάρτηση strcat χρησιμοποιείται για τη σύνδεση μιας συμβολοσειράς στην άλλη. Ας δούμε πώς; Με τη βοήθεια του παραδείγματος:
#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;
}
Και η εκτέλεση του προγράμματος θα είναι η εξής:
Enter a string: Data
Enter another string: Recovery
The two strings joined together: DataRecovery
Η συνάρτηση strtok
Η συνάρτηση strtok χρησιμοποιείται για την εύρεση του επόμενου διακριτικού σε μια συμβολοσειρά. Το διακριτικό καθορίζεται από μια λίστα πιθανών οριοθετητών.
Το ακόλουθο παράδειγμα διαβάζει μια γραμμή κειμένου από ένα αρχείο και προσδιορίζει μια λέξη χρησιμοποιώντας τους οριοθέτες, το διάστημα, την καρτέλα και τη νέα γραμμή. Στη συνέχεια, κάθε λέξη εμφανίζεται σε ξεχωριστή γραμμή:
#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;
}
Πάνω από το πρόγραμμα, στο = fopen("C:\\text.txt", "r"), ανοίγει και το υπάρχον αρχείο C:\\text.txt. Εάν δεν υπάρχει στην καθορισμένη διαδρομή ή για οποιονδήποτε λόγο, το αρχείο δεν μπορούσε να ανοίξει, εμφανίζεται ένα μήνυμα σφάλματος στην οθόνη.
Σκεφτείτε το ακόλουθο παράδειγμα, το οποίο χρησιμοποιεί ορισμένες από αυτές τις συναρτήσεις:
#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);
}
Η έξοδος του προγράμματος θα εμφανιστεί ως εξής:
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?
Λειτουργίες
Ο καλύτερος τρόπος για να αναπτύξετε και να διατηρήσετε ένα μεγάλο πρόγραμμα είναι να το κατασκευάσετε από μικρότερα κομμάτια, καθένα από τα οποία είναι πιο εύκολο στη διαχείριση (μια τεχνική που μερικές φορές αναφέρεται ως Divide and Conquer). Οι λειτουργίες επιτρέπουν στον προγραμματιστή να διαμορφώσει το πρόγραμμα.
Οι λειτουργίες επιτρέπουν σε πολύπλοκα προγράμματα να χωρίζονται σε μικρά μπλοκ, καθένα από τα οποία είναι ευκολότερο να γραφτεί, να διαβαστεί και να διατηρηθεί. Έχουμε ήδη συναντήσει τη λειτουργία main και χρησιμοποιήσαμε το printf από την τυπική βιβλιοθήκη. Μπορούμε φυσικά να φτιάξουμε τις δικές μας συναρτήσεις και αρχεία κεφαλίδων. Μια συνάρτηση έχει την ακόλουθη διάταξη:
return-type function-name ( argument list if necessary )
{
local-declarations;
statements ;
return return-value;
}
Εάν παραληφθεί ο τύπος επιστροφής, το C ορίζει από προεπιλογή int. Η τιμή επιστροφής πρέπει να είναι του δηλωμένου τύπου. Όλες οι μεταβλητές που δηλώνονται εντός συναρτήσεων ονομάζονται τοπικές μεταβλητές, καθώς είναι γνωστές μόνο στη συνάρτηση στην οποία έχουν οριστεί.
Ορισμένες συναρτήσεις έχουν μια λίστα παραμέτρων που παρέχει μια μέθοδο επικοινωνίας μεταξύ της συνάρτησης και της λειτουργικής μονάδας που κάλεσε τη συνάρτηση. Οι παράμετροι είναι επίσης τοπικές μεταβλητές, δεδομένου ότι δεν είναι διαθέσιμες εκτός της συνάρτησης. Τα προγράμματα που καλύπτονται μέχρι στιγμής έχουν όλα main, που είναι μια λειτουργία.
Μια συνάρτηση μπορεί απλώς να εκτελέσει μια εργασία χωρίς να επιστρέψει καμία τιμή, οπότε έχει την ακόλουθη διάταξη:
void function-name ( argument list if necessary )
{
local-declarations ;
statements;
}
Τα ορίσματα μεταβιβάζονται πάντα με τιμή στις κλήσεις συνάρτησης C. Αυτό σημαίνει ότι τα τοπικά αντίγραφα των τιμών των ορισμάτων μεταβιβάζονται στις ρουτίνες. Οποιαδήποτε αλλαγή γίνει στα ορίσματα εσωτερικά στη συνάρτηση γίνεται μόνο στα τοπικά αντίγραφα των ορισμάτων.
Για να αλλάξετε ή να ορίσετε ένα όρισμα στη λίστα ορισμάτων, αυτό το όρισμα πρέπει να μεταβιβαστεί ως διεύθυνση. Χρησιμοποιείτε κανονικές μεταβλητές εάν η συνάρτηση δεν αλλάζει τις τιμές αυτών των ορισμάτων. ΠΡΕΠΕΙ να χρησιμοποιήσετε δείκτες εάν η συνάρτηση αλλάζει τις τιμές αυτών των ορισμάτων.
Ας μάθουμε με παραδείγματα:
#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);
}
Και η έξοδος αυτού του προγράμματος θα εμφανιστεί ως εξής:
From main: a = 5, b = 7
From function exchange: a = 7, b = 5
Back in main: a = 7, b = 5
Ας δούμε ένα άλλο παράδειγμα. Το παρακάτω παράδειγμα χρησιμοποιεί μια συνάρτηση που ονομάζεται τετράγωνο και γράφει το τετράγωνο των αριθμών μεταξύ 1 και 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;
}
Η έξοδος αυτού του προγράμματος θα εμφανιστεί ως εξής:
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
Το τετράγωνο πρωτότυπου συνάρτησης δηλώνει μια συνάρτηση που παίρνει μια ακέραια παράμετρο και επιστρέφει έναν ακέραιο. Όταν ο μεταγλωττιστής φτάσει στην κλήση συνάρτησης στο τετράγωνο στο κύριο πρόγραμμα, μπορεί να ελέγξει την κλήση συνάρτησης σε σχέση με τον ορισμό της συνάρτησης.
Όταν το πρόγραμμα φτάσει στη γραμμή που καλεί τη συνάρτηση τετράγωνο, το πρόγραμμα μεταβαίνει στη συνάρτηση και την εκτελεί πριν συνεχίσει τη διαδρομή του μέσω του κύριου προγράμματος. Προγράμματα που δεν έχουν τύπο επιστροφής θα πρέπει να δηλώνονται χρησιμοποιώντας άκυρα. Έτσι, οι παράμετροι της συνάρτησης μπορεί να είναι Pass By Value ή Pass By Reference.
Μια αναδρομική συνάρτηση είναι μια συνάρτηση που καλεί τον εαυτό της. Και αυτή η διαδικασία ονομάζεται αναδρομή.
Pass By Value Functions
Οι παράμετροι της συνάρτησης τετραγώνου στο προηγούμενο παράδειγμα μεταβιβάζονται με τιμή. Αυτό σημαίνει ότι μόνο ένα αντίγραφο της μεταβλητής έχει περάσει στη συνάρτηση. Τυχόν αλλαγές στην τιμή δεν θα εμφανιστούν πίσω στη λειτουργία κλήσης.
Το ακόλουθο παράδειγμα χρησιμοποιεί τιμή pass-by-value και αλλάζει την τιμή της παραμέτρου που πέρασε, η οποία δεν επηρεάζει τη συνάρτηση κλήσης. Η συνάρτηση count_down έχει δηλωθεί ως άκυρη καθώς δεν υπάρχει τύπος επιστροφής.
#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');
}
Η έξοδος του προγράμματος θα εμφανιστεί ως εξής:
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
Ας δούμε ένα άλλο Παράδειγμα τιμής διέλευσης C για να το κατανοήσουμε καλύτερα. Το ακόλουθο παράδειγμα μετατρέπει έναν αριθμό μεταξύ 1 και 30.000 που πληκτρολογεί ο χρήστης σε λέξεις.
#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 ");
}
}
και η έξοδος του προγράμματος θα είναι η εξής:
Enter a number between 1 and 30,000: 12345
12345 in words = twelve thousand three hundred and forty five
Call-by-reference
Για να πραγματοποιήσετε κλήση προς αναφορά συνάρτησης, αντί να μεταβιβάσετε την ίδια τη μεταβλητή, περάστε τη διεύθυνση της μεταβλητής. Η διεύθυνση της μεταβλητής μπορεί να ληφθεί χρησιμοποιώντας το & χειριστής. Το παρακάτω καλεί μια συνάρτηση swap που μεταβιβάζει τη διεύθυνση των μεταβλητών αντί για τις πραγματικές τιμές.
swap(&x, &y);
Αποαναφορά
Το πρόβλημα που έχουμε τώρα είναι ότι η συνάρτηση swap έχει μεταβιβαστεί η διεύθυνση και όχι η μεταβλητή, επομένως πρέπει να αποαναφέρουμε τις μεταβλητές έτσι ώστε να κοιτάμε τις πραγματικές τιμές και όχι τις διευθύνσεις των μεταβλητών για να ανταλλάξουμε τους.
Η αποσύνδεση επιτυγχάνεται στο C χρησιμοποιώντας τη σημείωση του δείκτη (*). Με απλά λόγια, αυτό σημαίνει ότι τοποθετείτε ένα * πριν από κάθε μεταβλητή πριν τη χρησιμοποιήσετε, ώστε να αναφέρεται στην τιμή της μεταβλητής και όχι στη διεύθυνσή της. Το παρακάτω πρόγραμμα απεικονίζει την περαστική αναφορά για την εναλλαγή δύο τιμών.
#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;
}
Ας δούμε την έξοδο του προγράμματος:
Πριν από την εναλλαγή συνάρτησης, x = 6 και y = 10
Μετά την εναλλαγή συνάρτησης, x = 10 και y = 6
Οι συναρτήσεις μπορεί να είναι αναδρομικές που είναι μια συνάρτηση που μπορεί να καλέσει τον εαυτό της. Κάθε κλήση προς τον εαυτό της απαιτεί να προωθείται η τρέχουσα κατάσταση της συνάρτησης στη στοίβα. Είναι σημαντικό να θυμάστε αυτό το γεγονός, καθώς είναι εύκολο να δημιουργήσετε μια υπερχείλιση στοίβας, δηλαδή έχει εξαντληθεί ο χώρος στη στοίβα για να τοποθετήσετε περισσότερα δεδομένα.
Το ακόλουθο παράδειγμα υπολογίζει το παραγοντικό ενός αριθμού χρησιμοποιώντας αναδρομή. Ένα παραγοντικό είναι ένας αριθμός που πολλαπλασιάζεται με κάθε άλλο ακέραιο κάτω από τον εαυτό του, μέχρι το 1. Για παράδειγμα, το παραγοντικό του αριθμού 6 είναι:
Παραγοντικό 6 = 6 * 5 * 4 * 3 * 2 * 1
Επομένως, το παραγοντικό του 6 είναι 720. Μπορεί να φανεί από το παραπάνω παράδειγμα ότι παραγοντικό 6 = 6 * παραγοντικό 5. Ομοίως, παραγοντικό 5 = 5 * παραγοντικό 4 και ούτω καθεξής.
Ο ακόλουθος είναι ο γενικός κανόνας για τον υπολογισμό των παραγοντικών αριθμών.
factorial(n) = n * factorial(n-1)
Ο παραπάνω κανόνας τελειώνει όταν n = 1, καθώς το παραγοντικό του 1 είναι 1. Ας προσπαθήσουμε να τον κατανοήσουμε καλύτερα με τη βοήθεια του παραδείγματος:
#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);
}
Ας δούμε την έξοδο της εκτέλεσης αυτού του προγράμματος:
Enter a number: 7
factorial of 7 is 5040
Εκχώρηση μνήμης σε C
Ο μεταγλωττιστής C έχει μια βιβλιοθήκη εκχώρησης μνήμης, που ορίζεται στο malloc.h. Η μνήμη δεσμεύεται χρησιμοποιώντας τη συνάρτηση malloc και επιστρέφει έναν δείκτη στη διεύθυνση. Χρειάζεται μία παράμετρος, το μέγεθος της μνήμης που απαιτείται σε byte.
Το ακόλουθο παράδειγμα εκχωρεί χώρο για τη συμβολοσειρά, "hello world".
ptr = (char *)malloc(strlen("Hello world") + 1);
Το επιπλέον ένα byte απαιτείται για να ληφθεί υπόψη ο χαρακτήρας τερματισμού συμβολοσειράς, '\0'. Το (char *) ονομάζεται cast και αναγκάζει τον τύπο επιστροφής να είναι char *.
Καθώς οι τύποι δεδομένων έχουν διαφορετικά μεγέθη και το malloc επιστρέφει τον χώρο σε byte, είναι καλή πρακτική για λόγους φορητότητας να χρησιμοποιείται ο τελεστής sizeof κατά τον καθορισμό ενός μεγέθους προς εκχώρηση.
Το ακόλουθο παράδειγμα διαβάζει μια συμβολοσειρά στην προσωρινή μνήμη του πίνακα χαρακτήρων και στη συνέχεια εκχωρεί την ακριβή ποσότητα μνήμης που απαιτείται και την αντιγράφει σε μια μεταβλητή που ονομάζεται "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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
Enter a string: India is the best
You entered: India is the best
Ανακατανομή μνήμης
Είναι πιθανό πολλές φορές ενώ προγραμματίζετε να θέλετε να ανακατανείμετε τη μνήμη. Αυτό γίνεται με τη συνάρτηση realloc. Η συνάρτηση realloc λαμβάνει δύο παραμέτρους, τη βασική διεύθυνση της μνήμης που θέλετε να αλλάξετε το μέγεθος και τον χώρο που θέλετε να δεσμεύσετε και επιστρέφει έναν δείκτη στη διεύθυνση βάσης.
Ας υποθέσουμε ότι έχουμε δεσμεύσει χώρο για έναν δείκτη που ονομάζεται msg και θέλουμε να ανακατανείμουμε χώρο στον χώρο που καταλαμβάνει ήδη, συν το μήκος μιας άλλης συμβολοσειράς, τότε μπορούμε να χρησιμοποιήσουμε τα εξής.
msg = (char *)realloc(msg, (strlen(msg) + strlen(buffer) + 1)*sizeof(char));
Το παρακάτω πρόγραμμα απεικονίζει τη χρήση των malloc, realloc και free. Ο χρήστης εισάγει μια σειρά από συμβολοσειρές που ενώνονται μεταξύ τους. Το πρόγραμμα σταματά την ανάγνωση συμβολοσειρών όταν εισάγεται μια κενή συμβολοσειρά.
#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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
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
Αποδέσμευση μνήμης
Όταν τελειώσετε με τη μνήμη που έχει εκχωρηθεί, δεν πρέπει ποτέ να ξεχάσετε να ελευθερώσετε τη μνήμη, καθώς θα ελευθερωθούν πόροι και θα βελτιωθεί η ταχύτητα. Για να αποδεσμεύσετε την εκχωρημένη μνήμη, χρησιμοποιήστε τη λειτουργία δωρεάν.
free(ptr);
Δομές
Εκτός από τους βασικούς τύπους δεδομένων, το C διαθέτει μηχανισμό δομής που σας επιτρέπει να ομαδοποιήσετε στοιχεία δεδομένων που σχετίζονται μεταξύ τους με ένα κοινό όνομα. Αυτό αναφέρεται συνήθως ως τύπος που καθορίζεται από το χρήστη.
Η λέξη-κλειδί struct ξεκινά τον ορισμό της δομής και μια ετικέτα δίνει το μοναδικό όνομα στη δομή. Οι τύποι δεδομένων και τα ονόματα μεταβλητών που προστίθενται στη δομή είναι μέλη της δομής. Το αποτέλεσμα είναι ένα πρότυπο δομής που μπορεί να χρησιμοποιηθεί ως προσδιοριστής τύπου. Ακολουθεί μια δομή με ετικέτα μήνα.
struct month
{
char name[10];
char abbrev[4];
int days;
};
Ένας τύπος δομής ορίζεται συνήθως κοντά στην αρχή ενός αρχείου χρησιμοποιώντας μια δήλωση typedef. Το typedef ορίζει και ονομάζει έναν νέο τύπο, επιτρέποντας τη χρήση του σε όλο το πρόγραμμα. Το typedef εμφανίζεται συνήθως αμέσως μετά τις δηλώσεις #define and #include σε ένα αρχείο.
Η λέξη-κλειδί typedef μπορεί να χρησιμοποιηθεί για τον ορισμό μιας λέξης που αναφέρεται στη δομή αντί για τον καθορισμό της λέξης-κλειδιού struct με το όνομα της δομής. Είναι συνηθισμένο να ονομάζουμε το typedef με κεφαλαία γράμματα. Εδώ είναι τα παραδείγματα ορισμού δομής.
typedef struct {
char name[64];
char course[128];
int age;
int year;
} student;
Αυτό ορίζει έναν νέο τύπο Student μεταβλητές τύπου student μπορούν να δηλωθούν ως εξής.
μαθητής st_rec;
Παρατηρήστε πόσο παρόμοιο είναι αυτό με τη δήλωση int ή float. Το όνομα της μεταβλητής είναι st_rec, έχει μέλη που ονομάζονται όνομα, μάθημα, ηλικία και έτος. Ομοίως,
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;
Εξετάστε την ακόλουθη δομή:
struct student
{
char *name;
int grade;
};
Ένας δείκτης για τη δομή του μαθητή μπορεί να οριστεί ως εξής.
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;
Μπορείτε να ορίσετε έναν βαθμό στη δομή ως εξής.
s->grade = 50;
Όπως και με τους βασικούς τύπους δεδομένων, εάν θέλετε οι αλλαγές που γίνονται σε μια συνάρτηση σε παραμέτρους που έχουν περάσει να είναι επίμονες, πρέπει να περάσετε μέσω αναφοράς (διαβιβάστε τη διεύθυνση). Ο μηχανισμός είναι ακριβώς ο ίδιος με τους βασικούς τύπους δεδομένων. Περάστε τη διεύθυνση και ανατρέξτε στη μεταβλητή χρησιμοποιώντας σημειογραφία δείκτη.
Έχοντας ορίσει τη δομή, μπορείτε να δηλώσετε μια παρουσία της και να εκχωρήσετε τιμές στα μέλη χρησιμοποιώντας τον συμβολισμό κουκκίδων. Το ακόλουθο παράδειγμα επεξηγεί τη χρήση της δομής μήνα.
#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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
Ο Ιανουάριος συντομεύεται ως Ιαν και έχει 31 ημέρες
Όλοι οι μεταγλωττιστές ANSI C σάς επιτρέπουν να αντιστοιχίσετε μια δομή σε μια άλλη, εκτελώντας ένα αντίγραφο κατά μέλη. Εάν είχαμε δομές μήνα που ονομάζονταν m1 και m2, τότε θα μπορούσαμε να εκχωρήσουμε τις τιμές από m1 σε m2 με τα εξής:
- Δομή με μέλη του δείκτη.
- Η δομή αρχικοποιείται.
- Μεταβίβαση μιας δομής σε μια συνάρτηση.
- Δείκτες και δομές.
Δομές με μέλη του δείκτη στο C
Η διατήρηση συμβολοσειρών σε έναν πίνακα σταθερού μεγέθους είναι αναποτελεσματική χρήση της μνήμης. Μια πιο αποτελεσματική προσέγγιση θα ήταν η χρήση δεικτών. Οι δείκτες χρησιμοποιούνται σε δομές με τον ίδιο ακριβώς τρόπο που χρησιμοποιούνται στους κανονικούς ορισμούς δεικτών. Ας δούμε ένα παράδειγμα:
#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;
}
Η έξοδος του προγράμματος θα είναι η εξής:
Ο Ιανουάριος συντομεύεται ως Ιαν και έχει 31 ημέρες
Structure Initializers σε C
Για την παροχή ενός συνόλου αρχικών τιμών για τη δομή, μπορούν να προστεθούν Initializers στη δήλωση δήλωσης. Καθώς οι μήνες ξεκινούν από το 1, αλλά οι πίνακες ξεκινούν από το μηδέν στο C, ένα επιπλέον στοιχείο στη θέση μηδέν που ονομάζεται ανεπιθύμητη ενέργεια, έχει χρησιμοποιηθεί στο ακόλουθο παράδειγμα.
#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;
}
Και η έξοδος θα εμφανιστεί ως εξής:
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
Μεταβίβαση δομών σε συναρτήσεις στο C
Οι δομές μπορούν να μεταβιβαστούν ως παράμετροι σε μια συνάρτηση, όπως οποιοσδήποτε από τους βασικούς τύπους δεδομένων. Το παρακάτω παράδειγμα χρησιμοποιεί μια δομή που ονομάζεται ημερομηνία που έχει μεταβιβαστεί σε μια συνάρτηση isLeapYear για να προσδιοριστεί εάν το έτος είναι δίσεκτο.
Κανονικά μεταβιβάζατε μόνο την τιμή ημέρας, αλλά ολόκληρη η δομή μεταβιβάζεται για να απεικονίσει τη μεταβίβαση δομών σε συναρτήσεις.
#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;
}
Και η εκτέλεση του προγράμματος θα είναι η εξής:
Enter the date (eg: 11/11/1980): 9/12/1980
The date 9 December 1980 is a leap year
Το ακόλουθο παράδειγμα εκχωρεί δυναμικά έναν πίνακα δομών για την αποθήκευση των ονομάτων και του βαθμού των μαθητών. Στη συνέχεια, οι βαθμοί εμφανίζονται πίσω στον χρήστη με αύξουσα σειρά.
#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);
}
Η εκτέλεση της εξόδου θα είναι η εξής:
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 χρησιμοποιείται αντί για struct. Για να ορίσετε μια απλή ένωση μιας μεταβλητής char και μιας ακέραιας μεταβλητής, θα γράψετε τα εξής:
union shared {
char c;
int i;
};
Αυτή η ένωση, κοινόχρηστη, μπορεί να χρησιμοποιηθεί για τη δημιουργία περιπτώσεων μιας ένωσης που μπορεί να περιέχει είτε μια τιμή χαρακτήρα c είτε μια ακέραια τιμή i. Αυτή είναι μια συνθήκη OR. Σε αντίθεση με μια δομή που θα κρατούσε και τις δύο τιμές, η ένωση μπορεί να κρατήσει μόνο μία τιμή κάθε φορά.
Μια ένωση μπορεί να αρχικοποιηθεί στη δήλωσή της. Επειδή μόνο ένα μέλος μπορεί να χρησιμοποιηθεί κάθε φορά και μόνο ένα μπορεί να αρχικοποιηθεί. Για να αποφευχθεί η σύγχυση, μόνο το πρώτο μέλος της ένωσης μπορεί να αρχικοποιηθεί. Ο ακόλουθος κώδικας δείχνει μια παρουσία της κοινής ένωσης που δηλώνεται και αρχικοποιείται:
union shared generic_variable = {`@'};
Σημειώστε ότι η ένωση generic_variable αρχικοποιήθηκε ακριβώς όπως θα αρχικοποιηθεί το πρώτο μέλος μιας δομής.
Τα μεμονωμένα μέλη της ένωσης μπορούν να χρησιμοποιηθούν με τον ίδιο τρόπο που μπορούν να χρησιμοποιηθούν τα μέλη της δομής χρησιμοποιώντας τον τελεστή μέλους (.). Ωστόσο, υπάρχει μια σημαντική διαφορά στην πρόσβαση στα μέλη του σωματείου.
Μόνο ένα μέλος του σωματείου πρέπει να έχει πρόσβαση κάθε φορά. Επειδή ένα σωματείο αποθηκεύει τα μέλη του το ένα πάνω στο άλλο, είναι σημαντικό να έχετε πρόσβαση μόνο σε ένα μέλος κάθε φορά.
The union Keyword
union tag {
union_member(s);
/* additional statements may go here */
}instance;
Η λέξη-κλειδί ένωση χρησιμοποιείται για τη δήλωση συνδικάτων. Μια ένωση είναι μια συλλογή από μία ή περισσότερες μεταβλητές (union_members) που έχουν ομαδοποιηθεί κάτω από ένα μόνο όνομα. Επιπλέον, καθένα από αυτά τα μέλη της ένωσης καταλαμβάνει την ίδια περιοχή μνήμης.
Η λέξη-κλειδί ένωση προσδιορίζει την αρχή ενός ορισμού ένωσης. Ακολουθείται από μια ετικέτα που είναι το όνομα που δίνεται στο σωματείο. Μετά την ετικέτα είναι τα μέλη του σωματείου κλεισμένα σε άγκιστρα.
Ένα παράδειγμα, η πραγματική δήλωση μιας ένωσης, μπορεί επίσης να οριστεί. Εάν ορίσετε τη δομή χωρίς την παρουσία, είναι απλώς ένα πρότυπο που μπορεί να χρησιμοποιηθεί αργότερα σε ένα πρόγραμμα για τη δήλωση δομών. Ακολουθεί η μορφή ενός προτύπου:
union tag {
union_member(s);
/* additional statements may go here */
};
Για να χρησιμοποιήσετε το πρότυπο, θα χρησιμοποιήσετε την ακόλουθη μορφή:
παρουσία ετικέτας ένωσης;
Για να χρησιμοποιήσετε αυτήν τη μορφή, πρέπει να έχετε δηλώσει προηγουμένως μια ένωση με τη δεδομένη ετικέτα.
/* Δηλώστε ένα πρότυπο ένωσης που ονομάζεται ετικέτα */
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"};
Ας το καταλάβουμε καλύτερα με τη βοήθεια παραδειγμάτων:
#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;
}
Και η έξοδος του προγράμματος θα εμφανιστεί ως εξής:
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
Μια πρακτική χρήση μιας ένωσης στην ανάκτηση δεδομένων
Ας δούμε τώρα μια πρακτική χρήση της ένωσης είναι ο προγραμματισμός ανάκτησης δεδομένων. Ας πάρουμε ένα μικρό παράδειγμα. Το παρακάτω πρόγραμμα είναι το μικρό μοντέλο προγράμματος σάρωσης κατεστραμμένου τομέα για μονάδα δισκέτας (α: ), ωστόσο δεν είναι το πλήρες μοντέλο λογισμικού σάρωσης κατεστραμμένου τομέα.
Ας εξετάσουμε το πρόγραμμα:
#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;
}
Τώρα ας δούμε πώς θα είναι η έξοδος του εάν υπάρχει κακός τομέας στη δισκέτα:
Resetting the disk system....
Now Testing the Disk for Bad Sectors....
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
Μπορεί να είναι λίγο δύσκολο να κατανοήσουμε τις λειτουργίες και τις διακοπές που χρησιμοποιούνται σε αυτό το πρόγραμμα για την επαλήθευση του δίσκου για κατεστραμμένους τομείς και την επαναφορά του συστήματος του δίσκου κ.λπ., αλλά δεν χρειάζεται να ανησυχείτε, θα μάθουμε όλα αυτά τα πράγματα στο BIOS και θα διακόψουμε τον προγραμματισμό ενότητες αργότερα στα επόμενα κεφάλαια.
Χειρισμός αρχείων σε C
Η πρόσβαση στο αρχείο στο C επιτυγχάνεται με τη συσχέτιση μιας ροής με ένα αρχείο. Το C επικοινωνεί με αρχεία χρησιμοποιώντας έναν νέο τύπο δεδομένων που ονομάζεται δείκτης αρχείου. Αυτός ο τύπος ορίζεται στο stdio.h και γράφεται ως FILE *. Ένας δείκτης αρχείου που ονομάζεται output_file δηλώνεται σε μια δήλωση όπως
FILE *output_file;
Οι λειτουργίες αρχείων της λειτουργίας fopen
Το πρόγραμμά σας πρέπει να ανοίξει ένα αρχείο για να έχει πρόσβαση σε αυτό. Αυτό γίνεται χρησιμοποιώντας τη συνάρτηση fopen, η οποία επιστρέφει τον απαιτούμενο δείκτη αρχείου. Εάν το αρχείο δεν μπορεί να ανοίξει για οποιονδήποτε λόγο, τότε η τιμή NULL θα επιστραφεί. Συνήθως θα χρησιμοποιείτε το fopen ως εξής
if ((output_file = fopen("output_file", "w")) == NULL)
fprintf(stderr, "Cannot open %s\n",
"output_file");
Το fopen παίρνει δύο ορίσματα, και τα δύο είναι συμβολοσειρές, το πρώτο είναι το όνομα του αρχείου που θα ανοίξει, το δεύτερο είναι ένας χαρακτήρας πρόσβασης, ο οποίος είναι συνήθως ένας από τους r, a ή w κ.λπ. Τα αρχεία μπορούν να ανοίγουν σε διάφορους τρόπους, όπως φαίνεται στον παρακάτω πίνακα.
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. |
Οι λειτουργίες ενημέρωσης χρησιμοποιούνται με τις λειτουργίες fseek, fsetpos και rewind. Η συνάρτηση fopen επιστρέφει έναν δείκτη αρχείου ή NULL εάν παρουσιαστεί σφάλμα.
Το ακόλουθο παράδειγμα ανοίγει ένα αρχείο, tarun.txt σε λειτουργία μόνο για ανάγνωση. Είναι καλή πρακτική προγραμματισμού για να ελέγξετε ότι το αρχείο υπάρχει.
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
Κλείσιμο αρχείων
Τα αρχεία κλείνουν χρησιμοποιώντας τη συνάρτηση fclose. Η σύνταξη έχει ως εξής:
fclose(in);
Ανάγνωση αρχείων
Η συνάρτηση feof χρησιμοποιείται για τον έλεγχο του τέλους του αρχείου. Οι συναρτήσεις fgetc, fscanf και fgets χρησιμοποιούνται για την ανάγνωση δεδομένων από το αρχείο.
Το ακόλουθο παράδειγμα παραθέτει τα περιεχόμενα ενός αρχείου στην οθόνη, χρησιμοποιώντας το fgetc για να διαβάσετε το αρχείο έναν χαρακτήρα κάθε φορά.
#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;
}
Η συνάρτηση fscanf μπορεί να χρησιμοποιηθεί για την ανάγνωση διαφορετικών τύπων δεδομένων από το αρχείο, όπως στο παρακάτω παράδειγμα, με την προϋπόθεση ότι τα δεδομένα στο αρχείο έχουν τη μορφή της συμβολοσειράς μορφής που χρησιμοποιείται με το fscanf.
fscanf(in, "%d/%d/%d", &day, &month, &year);
Η συνάρτηση fgets χρησιμοποιείται για την ανάγνωση ενός αριθμού χαρακτήρων από ένα αρχείο. Το stdin είναι η τυπική ροή αρχείων εισόδου και η συνάρτηση fgets μπορεί να χρησιμοποιηθεί για τον έλεγχο της εισόδου.
Εγγραφή σε Αρχεία
Τα δεδομένα μπορούν να εγγραφούν στο αρχείο χρησιμοποιώντας fputc και fprintf. Το παρακάτω παράδειγμα χρησιμοποιεί τις συναρτήσεις fgetc και fputc για να δημιουργήσει ένα αντίγραφο ενός αρχείου κειμένου.
#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;
}
Η συνάρτηση fprintf μπορεί να χρησιμοποιηθεί για την εγγραφή μορφοποιημένων δεδομένων σε ένα αρχείο.
fprintf(out, "Date: %02d/%02d/%02d\n",
day, month, year);
Ορίσματα γραμμής εντολών με C
Ο ορισμός ANSI C για τη δήλωση της συνάρτησης main( ) είναι είτε:
int main() ή int main(int argc, char **argv)
Η δεύτερη έκδοση επιτρέπει τη μετάδοση ορισμάτων από τη γραμμή εντολών. Η παράμετρος argc είναι ένας μετρητής ορίσματος και περιέχει τον αριθμό των παραμέτρων που πέρασαν από τη γραμμή εντολών. Η παράμετρος argv είναι το διάνυσμα ορίσματος που είναι ένας πίνακας δεικτών σε συμβολοσειρές που αντιπροσωπεύουν τις πραγματικές παραμέτρους που έχουν περάσει.
Το ακόλουθο παράδειγμα επιτρέπει τη μετάδοση οποιουδήποτε αριθμού ορισμάτων από τη γραμμή εντολών και τα εκτυπώνει. Το argv[0] είναι το πραγματικό πρόγραμμα. Το πρόγραμμα πρέπει να εκτελείται από μια γραμμή εντολών.
#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.
Το επόμενο παράδειγμα χρησιμοποιεί τις ρουτίνες χειρισμού αρχείων για να αντιγράψει ένα αρχείο κειμένου σε ένα νέο αρχείο. Για παράδειγμα, το όρισμα της γραμμής εντολών θα μπορούσε να ονομαστεί ως:
txtcpy one.txt two.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;
}
Bitwise Manipulators
Σε επίπεδο υλικού, τα δεδομένα αντιπροσωπεύονται ως δυαδικοί αριθμοί. Η δυαδική αναπαράσταση του αριθμού 59 είναι 111011. Το bit 0 είναι το λιγότερο σημαντικό bit και σε αυτήν την περίπτωση το bit 5 είναι το πιο σημαντικό bit.
Κάθε σύνολο bit υπολογίζεται ως 2 στην ισχύ του συνόλου bit. Οι τελεστές bitwise σάς επιτρέπουν να χειρίζεστε ακέραιες μεταβλητές σε επίπεδο bit. Το παρακάτω δείχνει τη δυαδική αναπαράσταση του αριθμού 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 |
Με τρία bit, είναι δυνατή η αναπαράσταση των αριθμών 0 έως 7. Ο παρακάτω πίνακας δείχνει τους αριθμούς 0 έως 7 στη δυαδική τους μορφή.
Binary Digits |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
Ο παρακάτω πίνακας παραθέτει τους τελεστές bitwise που μπορούν να χρησιμοποιηθούν για τον χειρισμό δυαδικών αριθμών.
Binary Digits |
& |
Bitwise AND |
| |
Bitwise OR |
^ |
Bitwise Exclusive OR |
~ |
Bitwise Complement |
<< |
Bitwise Shift Left |
>> |
Bitwise Shift Right |
Bitwise AND
Το bitwise AND είναι True μόνο εάν έχουν οριστεί και τα δύο bit. Το παρακάτω παράδειγμα δείχνει το αποτέλεσμα ενός bitwise AND στους αριθμούς 23 και 12.
10111 (23)
01100 (12) AND
____________________
00100 (result = 4) |
Μπορείτε να χρησιμοποιήσετε μια τιμή μάσκας για να ελέγξετε εάν έχουν οριστεί ορισμένα bit. Αν θέλαμε να ελέγξουμε αν τα bit 1 και 3 έχουν οριστεί, θα μπορούσαμε να κρύψουμε τον αριθμό με το 10 (την τιμή των bit 1 και 3) και να ελέγξουμε το αποτέλεσμα με τη μάσκα.
#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;
}
Bitwise OR
Το bitwise OR είναι αληθές εάν έχουν οριστεί κάποιο από τα bit. Το παρακάτω δείχνει το αποτέλεσμα ενός bitwise OR στους αριθμούς 23 και 12.
10111 (23)
01100 (12) OR
______________________
11111 (result = 31) |
Μπορείτε να χρησιμοποιήσετε μια μάσκα για να βεβαιωθείτε ότι έχουν ρυθμιστεί ένα bit ή bits. Το ακόλουθο παράδειγμα διασφαλίζει ότι το bit 2 έχει οριστεί.
#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;
}
Bitwise Exclusive OR
Το Bitwise Exclusive OR είναι True αν έχουν οριστεί ένα από τα bit, αλλά όχι και τα δύο. Το παρακάτω δείχνει το αποτέλεσμα ενός Bitwise Exclusive OR στους αριθμούς 23 και 12.
10111 (23)
01100 (12) Exclusive OR (XOR)
_____________________________
11011 (result = 27) |
Το Exclusive OR έχει μερικές ενδιαφέρουσες ιδιότητες. Εάν είστε αποκλειστικός Ή ένας αριθμός από μόνος του, ορίζεται στο μηδέν, καθώς τα μηδενικά θα παραμείνουν μηδέν και τα μηδενικά δεν μπορούν να οριστούν και τα δύο, επομένως ορίζονται στο μηδέν.
Ως αποτέλεσμα αυτού, εάν έχετε Αποκλειστικό Ή έναν αριθμό με άλλο αριθμό, στη συνέχεια Αποκλειστικό Ή το αποτέλεσμα με τον άλλο αριθμό ξανά, το αποτέλεσμα είναι ο αρχικός αριθμός. Μπορείτε να το δοκιμάσετε με τους αριθμούς που χρησιμοποιούνται στο παραπάνω παράδειγμα.
23 XOR 12 = 27
27 XOR 12 = 23
27 XOR 23 = 12
Αυτή η δυνατότητα μπορεί να χρησιμοποιηθεί για κρυπτογράφηση. Το παρακάτω πρόγραμμα χρησιμοποιεί ένα κλειδί κρυπτογράφησης 23 για να απεικονίσει την ιδιότητα σε έναν αριθμό που έχει εισαχθεί από τον χρήστη.
#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;
}
Bitwise Compliment
Το bitwise Compliment είναι ένας τελεστής κομπλιμέντου που ενεργοποιεί ή απενεργοποιεί το bit. Εάν είναι 1, θα οριστεί στο 0, εάν είναι 0 θα οριστεί στο 1.
#include <stdio.h>
int main()
{
int num = 0xFFFF;
printf("The compliment of %X is %X\n", num, ~num);
return 0;
}
Bitwise Shift Left
Ο τελεστής Bitwise Shift Left μετατοπίζει τον αριθμό αριστερά. Τα πιο σημαντικά bit χάνονται καθώς ο αριθμός μετακινείται προς τα αριστερά και τα κενά λιγότερο σημαντικά bit είναι μηδέν. Το παρακάτω δείχνει τη δυαδική αναπαράσταση του 43.
0101011 (decimal 43)
Μετατοπίζοντας τα bit προς τα αριστερά, χάνουμε το πιο σημαντικό bit (σε αυτήν την περίπτωση, ένα μηδέν) και ο αριθμός συμπληρώνεται με ένα μηδέν στο λιγότερο σημαντικό bit. Ακολουθεί ο αριθμός που προκύπτει.
1010110 (decimal 86)
Bitwise Shift Right
Ο τελεστής Bitwise Shift Right μετατοπίζει τον αριθμό δεξιά. Το μηδέν εισάγεται στα εκκενωμένα πιο σημαντικά bit και τα κενά λιγότερο σημαντικά bit χάνονται. Το παρακάτω δείχνει τη δυαδική αναπαράσταση του αριθμού 43.
0101011 (δεκαδικός αριθμός 43)
Μετατοπίζοντας τα bit προς τα δεξιά, χάνουμε το λιγότερο σημαντικό bit (στην περίπτωση αυτή, ένα) και ο αριθμός συμπληρώνεται με ένα μηδέν στο πιο σημαντικό bit. Ακολουθεί ο αριθμός που προκύπτει.
0010101 (δεκαδικός αριθμός 21)
Το παρακάτω πρόγραμμα χρησιμοποιεί τα Bitwise Shift Right και Bitwise AND για να εμφανίσει έναν αριθμό ως δυαδικό αριθμό 16 bit. Ο αριθμός μετατοπίζεται διαδοχικά προς τα δεξιά από το 16 προς τα κάτω στο μηδέν και το Bitwise AND γίνεται με 1 για να δούμε αν το bit έχει οριστεί. Μια εναλλακτική μέθοδος θα ήταν η χρήση διαδοχικών μασκών με τον τελεστή Bitwise OR.
#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;
}
Λειτουργίες για Δυαδικές – Δεκαδικές Μετατροπές
Οι δύο συναρτήσεις που δίνονται στη συνέχεια είναι για μετατροπή Δυαδικού σε Δεκαδικό και Δεκαδικό σε Δυαδικό. Η συνάρτηση που δίνεται δίπλα στη μετατροπή ενός δεκαδικού αριθμού σε αντίστοιχο δυαδικό αριθμό υποστηρίζει έως και 32 – Bit Binary αριθμό. Μπορείτε να χρησιμοποιήσετε αυτό ή το πρόγραμμα που δόθηκε πριν για μετατροπή σύμφωνα με τις απαιτήσεις σας.
Συνάρτηση για μετατροπή δεκαδικού σε δυαδικό:
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");
}
Συνάρτηση για μετατροπή δυαδικού σε δεκαδικό:
Η ακόλουθη συνάρτηση είναι να μετατρέψει οποιονδήποτε Δυαδικό αριθμό στον αντίστοιχο δεκαδικό του αριθμό:
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");
}
Εντοπισμός σφαλμάτων και δοκιμή
Συντακτικά σφάλματα
Η σύνταξη αναφέρεται στη γραμματική, τη δομή και τη σειρά των στοιχείων σε μια δήλωση. Παρουσιάζεται ένα συντακτικό σφάλμα όταν παραβιάζουμε τους κανόνες, όπως ξεχνάμε να τελειώσουμε μια πρόταση με ερωτηματικό. Όταν κάνετε μεταγλώττιση του προγράμματος, ο μεταγλωττιστής θα παράγει μια λίστα με τυχόν συντακτικά σφάλματα που μπορεί να συναντήσει.
Ένας καλός μεταγλωττιστής θα παράγει τη λίστα με μια περιγραφή του σφάλματος και μπορεί να παρέχει μια πιθανή λύση. Η διόρθωση των σφαλμάτων μπορεί να έχει ως αποτέλεσμα την εμφάνιση περαιτέρω σφαλμάτων κατά την εκ νέου μεταγλώττιση. Ο λόγος για αυτό είναι ότι τα προηγούμενα σφάλματα άλλαξαν τη δομή του προγράμματος, πράγμα που σημαίνει ότι τα περαιτέρω σφάλματα καταργήθηκαν κατά τη διάρκεια της αρχικής μεταγλώττισης.
Ομοίως, ένα μόνο λάθος μπορεί να οδηγήσει σε πολλά σφάλματα. Δοκιμάστε να βάλετε ένα ερωτηματικό στο τέλος της κύριας συνάρτησης ενός προγράμματος που μεταγλωττίζεται και εκτελείται σωστά. Όταν το μεταγλωττίσετε εκ νέου, θα λάβετε μια τεράστια λίστα σφαλμάτων, και παρόλα αυτά είναι μόνο ένα άστοχο ερωτηματικό.
Εκτός από συντακτικά σφάλματα, οι μεταγλωττιστές ενδέχεται επίσης να εκδίδουν προειδοποιήσεις. Μια προειδοποίηση δεν είναι σφάλμα, αλλά μπορεί να προκαλέσει προβλήματα κατά την εκτέλεση του προγράμματός σας. Για παράδειγμα, η αντιστοίχιση ενός αριθμού κινητής υποδιαστολής διπλής ακρίβειας σε έναν αριθμό κινητής υποδιαστολής μονής ακρίβειας μπορεί να οδηγήσει σε απώλεια ακρίβειας. Δεν είναι συντακτικό σφάλμα, αλλά μπορεί να οδηγήσει σε προβλήματα. Σε αυτό το συγκεκριμένο παράδειγμα, θα μπορούσατε να δείξετε πρόθεση μεταφέροντας τη μεταβλητή στον κατάλληλο τύπο δεδομένων.
Εξετάστε το ακόλουθο παράδειγμα όπου x είναι αριθμός κινητής υποδιαστολής μονής ακρίβειας και y είναι αριθμός κινητής υποδιαστολής διπλής ακρίβειας. Το y μεταφέρεται ρητά σε ένα float κατά τη διάρκεια της ανάθεσης, κάτι που θα εξαλείφει τυχόν προειδοποιήσεις του μεταγλωττιστή.
x = (float)y;
Λογικά λάθη
Λογικά σφάλματα εμφανίζονται όταν υπάρχει σφάλμα στη λογική. Για παράδειγμα, θα μπορούσατε να ελέγξετε ότι ένας αριθμός είναι μικρότερος από 4 και μεγαλύτερος από 8. Αυτό δεν θα μπορούσε ποτέ να ισχύει, αλλά εάν είναι συντακτικά σωστό, το πρόγραμμα θα μεταγλωττιστεί με επιτυχία. Εξετάστε το ακόλουθο παράδειγμα:
if (x < 4 && x > 8)
puts("Will never happen!");
Η σύνταξη είναι σωστή, επομένως το πρόγραμμα θα μεταγλωττιστεί, αλλά η δήλωση puts δεν θα εκτυπωθεί ποτέ καθώς η τιμή του x δεν θα μπορούσε ενδεχομένως να είναι μικρότερη από τέσσερα και μεγαλύτερη από οκτώ ταυτόχρονα.
Τα περισσότερα λογικά σφάλματα ανακαλύπτονται μέσω της αρχικής δοκιμής του προγράμματος. Όταν δεν συμπεριφέρεται όπως περιμένατε, επιθεωρείτε τις λογικές δηλώσεις πιο προσεκτικά και τις διορθώνετε. Αυτό ισχύει μόνο για προφανή λογικά λάθη. Όσο μεγαλύτερο είναι το πρόγραμμα, τόσο περισσότερες διαδρομές θα υπάρχουν μέσα από αυτό, τόσο πιο δύσκολο γίνεται να επαληθευτεί ότι το πρόγραμμα συμπεριφέρεται όπως αναμένεται.
Δοκιμή
Στη διαδικασία ανάπτυξης λογισμικού, τα σφάλματα μπορούν να εισαχθούν σε οποιαδήποτε στάδια κατά την ανάπτυξη. Αυτό οφείλεται στο γεγονός ότι οι μέθοδοι επαλήθευσης των προηγούμενων φάσεων ανάπτυξης λογισμικού είναι χειροκίνητες. Ως εκ τούτου, ο κώδικας που αναπτύχθηκε κατά τη δραστηριότητα κωδικοποίησης είναι πιθανό να έχει ορισμένα σφάλματα απαιτήσεων και σφάλματα σχεδιασμού, επιπλέον των σφαλμάτων που εισάγονται κατά τη δραστηριότητα κωδικοποίησης. Κατά τη διάρκεια της δοκιμής, το πρόγραμμα που πρόκειται να δοκιμαστεί εκτελείται με ένα σύνολο δοκιμαστικών περιπτώσεων και η έξοδος του προγράμματος για τις δοκιμαστικές περιπτώσεις αξιολογείται για να προσδιοριστεί εάν αναμένεται η εκτέλεση του προγραμματισμού.
Έτσι, η δοκιμή είναι η διαδικασία ανάλυσης ενός στοιχείου λογισμικού για την ανίχνευση της διαφοράς μεταξύ των υφιστάμενων και των απαιτούμενων συνθηκών (δηλαδή, των σφαλμάτων) και για την αξιολόγηση των χαρακτηριστικών των στοιχείων λογισμικού. Έτσι, η δοκιμή είναι η διαδικασία ανάλυσης ενός προγράμματος με σκοπό την εύρεση σφαλμάτων.
Ορισμένες αρχές δοκιμών
- Η δοκιμή δεν μπορεί να δείξει την απουσία ελαττωμάτων, παρά μόνο την παρουσία τους.
- Όσο νωρίτερα γίνει ένα σφάλμα, τόσο πιο δαπανηρό είναι.
- Όσο αργότερα εντοπιστεί ένα σφάλμα, τόσο πιο ακριβό είναι.
Ας συζητήσουμε τώρα μερικές τεχνικές δοκιμών:
White Box Testing
Η δοκιμή λευκού κουτιού είναι μια τεχνική όπου όλες οι διαδρομές μέσω του προγράμματος ελέγχονται με κάθε δυνατή τιμή. Αυτή η προσέγγιση απαιτεί κάποια γνώση για το πώς πρέπει να συμπεριφέρεται το πρόγραμμα. Για παράδειγμα, εάν το πρόγραμμά σας δεχόταν μια ακέραια τιμή μεταξύ 1 και 50, μια δοκιμή λευκού πλαισίου θα δοκιμάσει το πρόγραμμα και με τις 50 τιμές για να βεβαιωθεί ότι ήταν σωστό για καθεμία και, στη συνέχεια, θα δοκίμαζε κάθε άλλη πιθανή τιμή που μπορεί να λάβει ένας ακέραιος. συμπεριφέρθηκε όπως αναμενόταν. Λαμβάνοντας υπόψη τον αριθμό των στοιχείων δεδομένων που μπορεί να έχει ένα τυπικό πρόγραμμα, οι πιθανές μεταθέσεις καθιστούν τη δοκιμή λευκού κουτιού εξαιρετικά δύσκολη για μεγάλα προγράμματα.
Η δοκιμή λευκού κουτιού μπορεί να εφαρμοστεί σε κρίσιμες λειτουργίες ασφαλείας ενός μεγάλου προγράμματος και πολλά από τα υπόλοιπα να ελεγχθούν χρησιμοποιώντας τη δοκιμή μαύρου κουτιού, που συζητείται παρακάτω. Λόγω του αριθμού των μεταθέσεων, η δοκιμή λευκού κουτιού συνήθως εκτελείται χρησιμοποιώντας μια ζώνη δοκιμής, όπου εύρη τιμών τροφοδοτούνται στο πρόγραμμα γρήγορα μέσω ενός ειδικού προγράμματος, καταγράφοντας εξαιρέσεις από την αναμενόμενη συμπεριφορά. Η δοκιμή λευκού κουτιού αναφέρεται μερικές φορές ως δομική, καθαρή ή ανοιχτή δοκιμή κουτιού.
Δοκιμή μαύρου κουτιού
Η δοκιμή μαύρου κουτιού είναι παρόμοια με τη δοκιμή λευκού κουτιού, εκτός από τη δοκιμή κάθε πιθανής τιμής, ελέγχονται οι επιλεγμένες τιμές. Σε αυτόν τον τύπο δοκιμής, ο ελεγκτής γνωρίζει τις εισροές και ποια θα πρέπει να είναι τα αναμενόμενα αποτελέσματα, αλλά όχι απαραίτητα πώς έφτασε σε αυτά το πρόγραμμα. Η δοκιμή μαύρου κουτιού αναφέρεται μερικές φορές ως λειτουργική δοκιμή.
Οι δοκιμές για τη δοκιμή μαύρου κουτιού συνήθως επινοούνται μόλις ολοκληρωθούν οι προδιαγραφές του προγράμματος. Οι περιπτώσεις δοκιμής βασίζονται σε τάξεις ισοδυναμίας.
Τάξεις ισοτιμίας
Για κάθε είσοδο, μια κλάση ισοδυναμίας προσδιορίζει τις έγκυρες και μη έγκυρες καταστάσεις. Υπάρχουν γενικά τρία σενάρια που πρέπει να προγραμματιστούν κατά τον καθορισμό κλάσεων ισοδυναμίας.
Εάν η είσοδος καθορίζει ένα εύρος ή μια συγκεκριμένη τιμή, θα οριστούν μία έγκυρη κατάσταση και δύο μη έγκυρες καταστάσεις. Για παράδειγμα, εάν ένας αριθμός πρέπει να είναι μεταξύ 1 και 20, η έγκυρη κατάσταση είναι μεταξύ 1 και 20, θα υπάρχει μη έγκυρη κατάσταση για λιγότερο από 1 και μη έγκυρη κατάσταση μεγαλύτερη από 20.
Εάν η είσοδος εξαιρεί ένα εύρος ή μια συγκεκριμένη τιμή, θα υπάρχουν δύο έγκυρες καταστάσεις και θα οριστεί μία μη έγκυρη κατάσταση. Για παράδειγμα, εάν ένας αριθμός δεν πρέπει να είναι μεταξύ 1 και 20, οι έγκυρες καταστάσεις είναι μικρότερες από μία και μεγαλύτερες από 20 και η μη έγκυρη κατάσταση είναι μεταξύ 1 και 20.
Εάν η είσοδος καθορίζει μια Boolean τιμή, θα υπάρχουν μόνο δύο καταστάσεις, μία έγκυρη και μία άκυρη.
Ανάλυση οριακής τιμής
Η ανάλυση οριακών τιμών λαμβάνει υπόψη μόνο τις τιμές στα όρια των εισόδων. Για παράδειγμα, στην περίπτωση που ένας αριθμός είναι μεταξύ 1 και 20, οι δοκιμαστικές περιπτώσεις μπορεί να είναι 1, 20, 0 και 21. Η σκέψη πίσω από αυτό είναι ότι εάν το πρόγραμμα λειτουργεί όπως αναμένεται με αυτές τις τιμές, οι άλλες τιμές θα λειτουργούν όπως αναμένεται.
Ο παρακάτω πίνακας παρέχει μια επισκόπηση των τυπικών ορίων που μπορεί να θέλετε να προσδιορίσετε.
Testing Ranges |
Input type |
Test Values |
Range |
- x[lower_bound]-1
- x[lower_bound]
- x[upper_bound]
- x[upper_bound]+1
|
Boolean |
|
Επινοώντας ένα σχέδιο δοκιμής
Προσδιορίστε τις κλάσεις ισοδυναμίας και για κάθε κλάση προσδιορίστε τα όρια. Αφού προσδιορίσετε τα όρια για την τάξη, γράψτε μια λίστα με έγκυρες και μη έγκυρες τιμές στο όριο και ποια θα πρέπει να είναι η αναμενόμενη συμπεριφορά. Στη συνέχεια, ο ελεγκτής μπορεί να εκτελέσει το πρόγραμμα με τις οριακές τιμές και να υποδείξει τι συνέβη όταν η οριακή τιμή δοκιμάστηκε σε σχέση με το απαιτούμενο αποτέλεσμα.
Το παρακάτω μπορεί να είναι ένα τυπικό πρόγραμμα δοκιμής που χρησιμοποιείται για τον έλεγχο της καταχώρισης μιας ηλικίας όπου οι αποδεκτές τιμές είναι στην περιοχή από 10 έως 110.
Equivalence Class |
Valid |
Invalid |
Between 10 and 110 |
> 110 |
|
< 10 |
Έχοντας ορίσει την τάξη ισοδυναμίας μας, μπορούμε τώρα να επινοήσουμε ένα σχέδιο δοκιμής για την ηλικία.
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 |
|
Το «Πραγματικό αποτέλεσμα» Η στήλη παραμένει κενή, καθώς θα συμπληρωθεί κατά τη δοκιμή. Εάν το αποτέλεσμα είναι το αναμενόμενο, η στήλη θα σημειωθεί. Εάν όχι, θα πρέπει να εισαχθεί ένα σχόλιο που να δείχνει τι συνέβη.
Σελίδα Τροποποιήθηκε στις: 07/01/2022