C Syntax

Η σύνταξη της C είναι η μορφή που πρέπει να έχει το κείμενο ώστε να αποτελεί έγκυρο κώδικα της γλώσσας προγραμματισμού C. Οι κανόνες σύνταξης της γλώσσας είναι σχεδιασμένοι ώστε να επιτρέπουν τη συγγραφή κώδικα που είναι περιεκτικός, έχει στενή σχέση με τον παραγόμενο αντικειμενικό κώδικα (object code) και ταυτόχρονα παρέχει σχετικά υψηλού επιπέδου αφαίρεση δεδομένων (data abstraction). Η C ήταν η πρώτη ευρέως επιτυχημένη γλώσσα υψηλού επιπέδου για τη φορητή ανάπτυξη λειτουργικών συστημάτων.[1] Η σύνταξη της C κάνει χρήση της αρχής του maximal munch.
Ως γλώσσα ελεύθερης μορφής (free-form language), ο κώδικας C μπορεί να μορφοποιηθεί με διαφορετικούς τρόπους χωρίς να επηρεάζεται η συντακτική του φύση. Η σύνταξη της C επηρέασε τη σύνταξη πολλών μεταγενέστερων γλωσσών, συμπεριλαμβανομένων των C++, Java και C#.
Δομή υψηλού επιπέδου
[Επεξεργασία | επεξεργασία κώδικα]Ο κώδικας C αποτελείται από οδηγίες του προεπεξεργαστή, τύπους, μεταβλητές και συναρτήσεις της κύριας γλώσσας, οργανωμένα σε ένα ή περισσότερα αρχεία πηγής. Η κατασκευή (building) του κώδικα περιλαμβάνει συνήθως την προεπεξεργασία και στη συνέχεια τη μεταγλώττιση (compiling) κάθε αρχείου πηγής σε ένα αντικειμενικό αρχείο (object file). Στη συνέχεια, τα αντικειμενικά αρχεία συνδέονται (linked) για τη δημιουργία μιας εκτελέσιμης εικόνας.
Οι μεταβλητές και οι συναρτήσεις μπορούν να δηλωθούν ξεχωριστά από τον ορισμό τους. Μια δήλωση (declaration) προσδιορίζει το όνομα ενός στοιχείου ορισμένου από τον χρήστη και κάποιες πληροφορίες για το πώς το στοιχείο μπορεί να χρησιμοποιηθεί κατά το χρόνο εκτέλεσης. Ένας ορισμός (definition) είναι μια πλήρης περιγραφή ενός στοιχείου που περιλαμβάνει τη δήλωση καθώς και πρόσθετες πληροφορίες. Για παράδειγμα, μια δήλωση συνάρτησης υποδεικνύει το όνομα και προαιρετικά τον τύπο και τα ορίσματα. Ένας ορισμός συνάρτησης περιλαμβάνει τον κώδικα υλοποίησης.
Σημείο εισόδου
[Επεξεργασία | επεξεργασία κώδικα]
Για ένα «φιλοξενούμενο» (hosted) περιβάλλον, ένα πρόγραμμα ξεκινά από μια συνάρτηση σημείου εισόδου με το όνομα main. Η συνάρτηση πρέπει να δηλώνεται σύμφωνα με ένα από τα ακόλουθα πρωτότυπα:
int main();
int main(void);
int main(int argc, char* argv[]);
int main(int argc, char** argv);
Η τιμή επιστροφής, τύπου int, χρησιμεύει ως δείκτης κατάστασης προς το περιβάλλον φιλοξενίας. Ορισμένη στο <stdlib.h>, η βιβλιοθήκη παρέχει τις μακροεντολές EXIT_SUCCESS και EXIT_FAILURE.
Ένα ελάχιστο πρόγραμμα αποτελείται από μια κενή συνάρτηση main:
int main() {}
Σε αντίθεση με άλλες συναρτήσεις, η γλώσσα απαιτεί το πρόγραμμα να ενεργεί σαν να επιστρέφει 0 ακόμη και αν δεν καταλήγει σε δήλωση return (από το πρότυπο C99).
Ορίσματα γραμμής εντολών
[Επεξεργασία | επεξεργασία κώδικα]Τα ορίσματα μεταβιβάζονται ως δύο τιμές: τον αριθμό των ορισμάτων (argc) και έναν πίνακα από συμβολοσειρές (argv), με το όνομα του προγράμματος ως πρώτο στοιχείο.
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("argc\t= %d\n", argc);
for (int i = 0; i < argc; ++i) {
printf("argv[%i]\t= %s\n", i, argv[i]);
}
}
Δεσμευμένες λέξεις
[Επεξεργασία | επεξεργασία κώδικα]Οι ακόλουθες 43 λέξεις είναι δεσμευμένες (reserved) — δεν επιτρέπεται να χρησιμοποιηθούν ως αναγνωριστικά (identifiers).
alignasalignofautoboolbreakcasecharconstconstexprcontinuedefaultdodoubleelseenumexternfloatforgotoifinlineintlongregisterrestrictreturnshortsignedsizeofstaticstatic_assertstructswitchthread_localtypedeftypeoftypeof_unqualunionunsignedvoidvolatilewhile
Οι ακόλουθες 14 λέξεις-κλειδιά αντικαθίστανται συχνά από μια μακροεντολή ή μια κατάλληλη λέξη από την παραπάνω λίστα. Ορισμένες από αυτές θεωρούνται παρωχημένες (deprecated) από το πρότυπο C23.
_Alignas(παρωχημένο)_Alignof(παρωχημένο)_Atomic_BitInt_Bool(παρωχημένο)_Complex_Countof_Decimal32_Decimal64_Decimal128_Generic_Noreturn(παρωχημένο)_Static_assert(παρωχημένο)_Thread_local(παρωχημένο)
Η λέξη-κλειδί _Imaginary αφαιρέθηκε στην έκδοση C2Y.
Οι ακόλουθες 3 λέξεις αναφέρονται σε κυριολεκτικές τιμές (literals) που χρησιμοποιούνται από τη γλώσσα:
nullptrtruefalse
Οι διάφορες υλοποιήσεις ενδέχεται να δεσμεύουν επιπλέον λέξεις-κλειδιά, οι οποίες συνήθως ξεκινούν με μία ή δύο κάτω παύλες. Οι παρακάτω θεωρούνται επεκτάσεις (extensions):
Οδηγίες προεπεξεργαστή
[Επεξεργασία | επεξεργασία κώδικα]Οι ακόλουθες 19 οδηγίες αφορούν τον προεπεξεργαστή της C.
#if#elif#else#endif#ifdef#ifndef#elifdef#elifndef#define#undef#include#embed#line#error#warning#pragma#__has_include#__has_embed#__has_c_attribute
Ο τελεστής _Pragma παρέχει μια εναλλακτική σύνταξη για τη λειτουργικότητα που προσφέρει η οδηγία #pragma.
Σχόλια
[Επεξεργασία | επεξεργασία κώδικα]Τα σχόλια είναι πληροφοριακό κείμενο για τον προγραμματιστή που αγνοείται από τον μεταγλωττιστή. Στη C υποστηρίζονται δύο είδη σύνταξης: τα σχόλια γραμμής και τα σχόλια μπλοκ. Ένα σχόλιο γραμμής ξεκινά με // και τελειώνει στο τέλος της ίδιας γραμμής. Ένα σχόλιο μπλοκ ξεκινά με /* και τελειώνει με */, και μπορεί να εκτείνεται σε οποιοδήποτε αριθμό γραμμών.
Τα σχόλια δεν μπορούν να είναι εμφωλευμένα (nested). Για παράδειγμα, το /* μέσα σε ένα σχόλιο γραμμής δεν αντιμετωπίζεται ως έναρξη σχολίου μπλοκ, και το // μέσα σε ένα σχόλιο μπλοκ δεν ξεκινά σχόλιο γραμμής. Η σύνταξη σχολίων γραμμής (στυλ C++) εισήχθη στο πρότυπο C99 και δεν ήταν διαθέσιμη στις αρχικές εκδόσεις K&R ή ANSI C.
int i; // σχόλιο γραμμής
/* σχόλιο
μπλοκ */
int ii = /* πάντα μηδέν */ 0;
Επειδή τα σχόλια δεν εμφωλεύονται, ο παρακάτω κώδικας δεν είναι έγκυρος, καθώς ο μεταγλωττιστής θα θεωρήσει ότι το σχόλιο τελειώνει στην πρώτη εμφάνιση του */ (γραμμή 5), αφήνοντας την υπόλοιπη γραμμή 6 ως λανθασμένο κώδικα:
/*
Έναρξη σχολίου
/*
Εσωτερικό μπλοκ (πρόθεση προγραμματιστή)
*/
Ο μεταγλωττιστής θεωρεί αυτή τη γραμμή ως κώδικα!
*/
Αναγνωριστικά
[Επεξεργασία | επεξεργασία κώδικα]Η σύνταξη υποστηρίζει αναγνωριστικά (identifiers) ορισμένα από τον χρήστη. Ένα αναγνωριστικό πρέπει να ξεκινά με γράμμα (A-Z, a-z) ή κάτω παύλα (_). Οι επόμενοι χαρακτήρες μπορεί να είναι γράμματα, αριθμοί (0-9) ή κάτω παύλες και δεν πρέπει να είναι δεσμευμένη λέξη. Τα αναγνωριστικά έχουν ευαισθησία ως προς τα κεφαλαία/πεζά γράμματα (case sensitive), καθιστώντας τα foo, FOO και Foo διακριτά μεταξύ τους.
Σειρά αξιολόγησης
[Επεξεργασία | επεξεργασία κώδικα]Το πρότυπο ορίζει ότι οι εκφράσεις μπορούν να αξιολογηθούν με οποιαδήποτε σειρά μεταξύ των σημείων ακολουθίας (sequence points). Τα σημεία ακολουθίας ορίζονται ως εξής:
- Τέλος εντολής (Statement end).
- Τελεστής διαχωρισμού: το κόμμα (τα κόμματα που διαχωρίζουν ορίσματα συναρτήσεων δεν αποτελούν σημεία ακολουθίας).
- Τελεστές βραχυκυκλώματος: το λογικό «και» (
&&) και το λογικό «ή» (). - Τριαδικός τελεστής (
?:): Αξιολογεί πρώτα την πρώτη υποέκφραση και μετά τη δεύτερη ή την τρίτη (ποτέ και τις δύο). - Είσοδος και έξοδος από μια κλήση συνάρτησης.
Στην περίπτωση της βραχυκυκλωμένης αξιολόγησης (short-circuit evaluation), η δεύτερη έκφραση ενδέχεται να μην αξιολογηθεί. Για παράδειγμα, στην έκφραση, αν η πρώτη συνάρτηση επιστρέψει μη μηδενική τιμή (αληθές), η b() δεν θα κληθεί ποτέ.
Τα ορίσματα σε μια κλήση συνάρτησης μπορούν να αξιολογηθούν με οποιαδήποτε σειρά. Ο παρακάτω κώδικας έχει απροσδιόριστη συμπεριφορά (undefined behavior):
printf("%s %s\n", argv[i = 0], argv[++i]);
Συμπερίληψη κώδικα
[Επεξεργασία | επεξεργασία κώδικα]Κεφαλίδες (Headers)
[Επεξεργασία | επεξεργασία κώδικα]Η συμπερίληψη κώδικα από άλλα αρχεία γίνεται με την οδηγία #include, η οποία αντιγράφει το περιεχόμενο του αρχείου απευθείας στο σημείο της δήλωσης. Παραδοσιακά, ο κώδικας C χωρίζεται σε αρχεία κεφαλίδας (με επέκταση .h) και αρχεία πηγής (με επέκταση .c). Η κεφαλίδα περιέχει τις δηλώσεις των συμβόλων, ενώ το αρχείο πηγής περιέχει την πλήρη υλοποίηση. Αυτός ο διαχωρισμός αποτρέπει την επανειλημμένη μεταγλώττιση του ίδιου κώδικα, μειώνοντας τους χρόνους δόμησης. Καθώς η C δεν διαθέτει χώρους ονομάτων (namespaces), όλα τα ονόματα συμβόλων είναι καθολικά και θα υπάρξει σύγκρουση αν το ίδιο όνομα χρησιμοποιηθεί για πολλαπλά αντικείμενα στην ίδια μονάδα μετάφρασης.
Για την αποφυγή πολλαπλής συμπερίληψης της ίδιας κεφαλίδας χρησιμοποιούνται οι φύλακες συμπερίληψης (#include guards) ή η οδηγία #pragma once.
#pragma once
// Συμπερίληψη πρότυπης βιβλιοθήκης (αναζήτηση σε καταλόγους συστήματος)
#include <stdio.h>
#include <stdlib.h>
// Τοπικές κεφαλίδες (αναζήτηση σε σχετική διαδρομή)
#include "Personalities.h"
#include "Math/SpecialFunctions.h"
// Εξωτερικές βιβλιοθήκες
#include <sqlite3.h>
Ενσωμάτωση (Embed)
[Επεξεργασία | επεξεργασία κώδικα]Από το πρότυπο C23, η οδηγία #embed επιτρέπει την άμεση ενσωμάτωση δυαδικών δεδομένων (binary content) σε ένα αρχείο, ακόμη και αν αυτά δεν αποτελούν έγκυρο κώδικα C. Αυτό είναι ιδιαίτερα χρήσιμο για την ενσωμάτωση εικόνων, αρχείων ρυθμίσεων ή άλλων πόρων απευθείας στο εκτελέσιμο.
const unsigned char iconDisplayData[] = {
#embed "art.png"
};
// Οποιοσδήποτε τύπος που αρχικοποιείται από ακέραιες σταθερές είναι αποδεκτός
const char resetBlob[] = {
#embed "data.bin"
};
int main() {
return
#embed </dev/urandom> limit(1)
;
}
Ενότητες Clang (Clang C modules)
[Επεξεργασία | επεξεργασία κώδικα]Ο μεταγλωττιστής Clang προσφέρει μια μη τυποποιημένη δυνατότητα που ονομάζεται «ενότητες» (modules). Παρόμοιες με τις ενότητες της C++, χρησιμοποιούνται για τη βελτίωση του χρόνου μεταγλώττισης, επιτρέποντας στο σύστημα να μεταγλωττίζει μια μονάδα μετάφρασης μόνο μία φορά.
Σύστημα τύπων
[Επεξεργασία | επεξεργασία κώδικα]Πρωτογενείς τύποι
[Επεξεργασία | επεξεργασία κώδικα]Η γλώσσα υποστηρίζει πρωτογενείς αριθμητικούς τύπους για ακέραιες και πραγματικές τιμές, οι οποίες συνήθως αντιστοιχίζονται απευθείας στην αρχιτεκτονική του συνόλου εντολών της ΚΜΕ (CPU). Οι ακέραιοι τύποι αποθηκεύουν τιμές σε ένα υποσύνολο των ακεραίων, ενώ οι πραγματικοί τύποι αποθηκεύουν τιμές σε ένα υποσύνολο των πραγματικών αριθμών σε μορφή κινητής υποδιαστολής. Ένας μιγαδικός τύπος δεδομένων αποθηκεύει δύο πραγματικές τιμές.
Οι ακέραιοι τύποι διαθέτουν παραλλαγές με πρόσημο (signed) και χωρίς πρόσημο (unsigned). Εάν δεν προσδιοριστεί τίποτα, θεωρείται ότι ο τύπος είναι signed. Ωστόσο, για ιστορικούς λόγους, ο τύπος char είναι διακριτός τόσο από τον signed char όσο και από τον unsigned char. Μπορεί να έχει ή να μην έχει πρόσημο, ανάλογα με τον μεταγλωττιστή και το σύνολο χαρακτήρων.
Ακέραιοι τύποι
[Επεξεργασία | επεξεργασία κώδικα]Οι ακέραιοι τύποι διατίθενται σε διαφορετικά σταθερά μεγέθη. Ο τύπος char καταλαμβάνει ακριβώς ένα byte (τη μικρότερη διευθυνσιοδοτήσιμη μονάδα αποθήκευσης), η οποία είναι συνήθως εύρους 8 bit. Οι υπογεγραμμένοι ακέραιοι τύποι χρησιμοποιούν πάντα την αναπαράσταση συμπλήρωμα ως προς δύο, από την έκδοση C23 και μετά.
Ο ακόλουθος πίνακας παραθέτει τους τυπικούς ακέραιους τύπους με το ελάχιστο επιτρεπτό εύρος σε bit.
| Όνομα | Ελάχιστο εύρος (bits) |
|---|---|
bool |
1 |
char |
8 |
signed char |
8 |
unsigned char |
8 |
short |
16 |
unsigned short |
16 |
int |
16 |
unsigned int |
16 |
long |
32 |
unsigned long |
32 |
long long |
64 |
unsigned long long |
64 |
Το εύρος και η αναπαράσταση κάθε τύπου επιλέγονται με βάση την αρχιτεκτονική του μηχανήματος. Το εύρος του τύπου int ποικίλλει ιδιαίτερα· συχνά αντιστοιχεί στο "φυσικό" μέγεθος λέξης (word size) της πλατφόρμας. Η πρότυπη κεφαλίδα <limits.h> ορίζει μακροεντολές για τις ελάχιστες και μέγιστες τιμές αυτών των τύπων. Για ακριβέστερο καθορισμό εύρους, οι προγραμματιστές μπορούν να χρησιμοποιούν τους τύπους από την κεφαλίδα (π.χ. int32_t).
Οι ακέραιες σταθερές μπορούν να οριστούν στο δεκαδικό σύστημα (π.χ. 1022), στο οκταδικό με το πρόθεμα 0 (π.χ. 01776) ή στο δεκαεξαδικό με το πρόθεμα 0x (π.χ. 0x3FE). Ένας χαρακτήρας σε μονά εισαγωγικά (π.χ. 'R') αντιπροσωπεύει την ακέραια τιμή αυτού του χαρακτήρα στο σύνολο χαρακτήρων εκτέλεσης.
Αριθμημένος τύπος
[Επεξεργασία | επεξεργασία κώδικα]Ο αριθμημένος τύπος (enumerated type), ο οποίος ορίζεται με τη λέξη-κλειδί enum, είναι ένας τύπος σχεδιασμένος να αναπαριστά τιμές μέσα από μια σειρά ονομαστικών σταθερών. Κάθε μία από τις σταθερές αυτές είναι τύπου int. Ο ίδιος ο τύπος enum είναι συμβατός με τον char ή με υπογεγραμμένους/μη υπογεγραμμένους ακέραιους τύπους, αλλά κάθε υλοποίηση ορίζει τους δικούς της κανόνες για την επιλογή του κατάλληλου τύπου.
Οι σταθερές enum χρησιμοποιούνται συχνά αντί της οδηγίας #define του προεπεξεργαστή για τη δημιουργία ονομαστικών σταθερών. Τέτοιες σταθερές είναι γενικά ασφαλέστερες στη χρήση από τις μακροεντολές, καθώς παραμένουν εντός του χώρου ονομάτων των αναγνωριστικών.
Ένας αριθμημένος τύπος δηλώνεται με τον προσδιοριστή enum και ένα προαιρετικό όνομα (ετικέτα), ακολουθούμενο από μια λίστα σταθερών μέσα σε άγκιστρα. Από προεπιλογή, στην πρώτη σταθερά εκχωρείται η τιμή μηδέν και κάθε επόμενη τιμή αυξάνεται κατά ένα. Μπορούν επίσης να εκχωρηθούν συγκεκριμένες τιμές στις σταθερές κατά τη δήλωση.
Παράδειγμα δήλωσης:
enum Color {
RED, // 0
GREEN, // 1
BLUE = 5,
YELLOW // 6
} paint_color;
Σε αντίθεση με τη C++, οι απαριθμήσεις στη C δεν έχουν εμβέλεια (scoped), καθώς η C δεν διαθέτει την έννοια των namespaces. Στη C, τα enum μπορούν να μετατραπούν σιωπηρά σε αριθμητικούς τύπους, γεγονός που θεωρείται μη ασφαλές ως προς τους τύπους (type-unsafe).
typedef enum Color {
RED,
ORANGE,
YELLOW,
GREEN,
BLUE,
INDIGO,
VIOLET
} Color;
Color c = RED; // Έγκυρο στη C
Color d = Color::RED; // Έγκυρο στη C++, αλλά ΟΧΙ στη C
Από το πρότυπο C23, είναι πλέον δυνατός ο χειροκίνητος καθορισμός του υποκείμενου τύπου για ένα enum, όπως συμβαίνει και στη C++.
enum CardSuit: char {
HEARTS,
CLUBS,
SPADES,
DIAMONDS
};
Τύποι κινητής υποδιαστολής
[Επεξεργασία | επεξεργασία κώδικα]Μια μορφή κινητής υποδιαστολής χρησιμοποιείται για την αναπαράσταση αριθμών με κλασματικό μέρος. Ωστόσο, δεν αναπαριστούν τους περισσότερους ρητούς αριθμούς με ακρίβεια, αλλά αποτελούν μια κοντινή προσέγγιση. Υπάρχουν τρεις τυπικοί τύποι πραγματικών τιμών (και από το πρότυπο C23 τρεις επιπλέον δεκαδικοί τύποι): μονής ακρίβειας (float), διπλής ακρίβειας (double) και διπλής εκτεταμένης ακρίβειας (long double). Καθένας από αυτούς μπορεί να αναπαριστά τιμές σε διαφορετική μορφή, συχνά σε μία από τις μορφές IEEE κινητής υποδιαστολής.
| Προσδιοριστές τύπου | Ακρίβεια (δεκαδικά ψηφία) | Εύρος εκθέτη | ||
|---|---|---|---|---|
| Ελάχιστο | IEEE 754 | Ελάχιστο | IEEE 754 | |
float |
6 | 7.2 (24 bits) | ±37 | ±38 (8 bits) |
double |
10 | 15.9 (53 bits) | ±37 | ±307 (11 bits) |
long double |
10 | 34.0 (113 bits) | ±37 | ±4931 (15 bits) |
Οι σταθερές κινητής υποδιαστολής μπορούν να γραφτούν σε δεκαδική μορφή (π.χ. 1.23). Μπορεί επίσης να χρησιμοποιηθεί η επιστημονική σημειογραφία προσθέτοντας e ή E ακολουθούμενο από έναν δεκαδικό εκθέτη (π.χ. 1.23e2, που έχει την τιμή 1.23 × 102 = 123.0). Απαιτείται είτε υποδιαστολή είτε εκθέτης (διαφορετικά, ο αριθμός αναλύεται ως ακέραια σταθερά).
Οι δεκαεξαδικές σταθερές κινητής υποδιαστολής έχουν το πρόθεμα 0x και χρησιμοποιούν το p ή P για τον καθορισμό δυαδικού εκθέτη (π.χ. 0xAp-2, που έχει την τιμή 2.5, αφού Ah × 2−2 = 10 ÷ 4). Οι σταθερές μπορούν να έχουν την κατάληξη f ή F για τον τύπο float, l ή L για τον τύπο long double, ή να παραμείνουν χωρίς κατάληξη για τον τύπο double.
Η πρότυπη κεφαλίδα <float.h> ορίζει τις ελάχιστες και μέγιστες τιμές των τύπων κινητής υποδιαστολής της υλοποίησης, καθώς και άλλους σχετικούς περιορισμούς.
Το πρότυπο C23 εισάγει τρεις πρόσθετους «δεκαδικούς» (σε αντίθεση με τους δυαδικούς) τύπους πραγματικής κινητής υποδιαστολής: _Decimal32, _Decimal64 και _Decimal128. Ιστορικά, η βάση (radix) της C ήταν το δυαδικό σύστημα (βάση 2), πράγμα που σημαίνει ότι αριθμοί όπως το 1/2 ή το 1/4 είναι ακριβείς, αλλά όχι το 1/10 ή το 1/3. Με τη δεκαδική κινητή υποδιαστολή, αριθμοί όπως το 1/10 και το 1/100 είναι ακριβείς, αλλά και πάλι όχι το 1/3.
Κατηγορία αποθήκευσης
[Επεξεργασία | επεξεργασία κώδικα]Ο ακόλουθος πίνακας περιγράφει τους προσδιοριστές που ορίζουν διάφορες ιδιότητες αποθήκευσης, συμπεριλαμβανομένης της διάρκειας ζωής (static, automatic ή dynamic).
| Προσδιοριστής | Διάρκεια ζωής | Εμβέλεια (Scope) | Προεπιλεγμένη αρχικοποίηση |
|---|---|---|---|
auto |
Μπλοκ (στοίβα - stack) | Μπλοκ | Μη αρχικοποιημένο |
register |
Μπλοκ (στοίβα ή καταχωρητής ΚΜΕ) | Μπλοκ | Μη αρχικοποιημένο |
static |
Πρόγραμμα | Μπλοκ ή μονάδα μετάφρασης | Μηδέν |
extern |
Πρόγραμμα | Καθολική (ολόκληρο το πρόγραμμα) | Μηδέν |
thread_local |
Νήμα (Thread) | - | - |
| (κανένας)1 | Δυναμική (σωρός - heap) | - | Μη αρχικοποιημένο (ή 0 με χρήση της calloc()) |
- 1 Δέσμευση και αποδέσμευση μέσω των συναρτήσεων της βιβλιοθήκης
malloc()καιfree().
Οι μεταβλητές που δηλώνονται μέσα σε ένα μπλοκ έχουν από προεπιλογή αυτόματη αποθήκευση, όπως και αυτές που δηλώνονται ρητά με τους προσδιοριστές auto ή register. Ο προσδιοριστής auto είναι συνήθως πλεονασματικός. Αντικείμενα που δηλώνονται έξω από όλα τα μπλοκ ή ρητά με τον προσδιοριστή static, έχουν στατική διάρκεια ζωής και αρχικοποιούνται στο μηδέν από τον μεταγλωττιστή.
Τα αντικείμενα με αυτόματη αποθήκευση είναι τοπικά στο μπλοκ τους και καταστρέφονται με την έξοδο από αυτό. Ο προσδιοριστής register υποδεικνύει στον μεταγλωττιστή να δώσει προτεραιότητα στην αποθήκευση της μεταβλητής σε καταχωρητές της ΚΜΕ για ταχύτερη πρόσβαση. Τα αντικείμενα αυτής της κατηγορίας δεν μπορούν να χρησιμοποιηθούν με τον τελεστή διεύθυνσης (&).
Ο προσδιοριστής extern υποδηλώνει ότι ο χώρος αποθήκευσης ενός αντικειμένου έχει οριστεί αλλού. Όταν χρησιμοποιείται μέσα σε μπλοκ, δείχνει ότι το αντικείμενο έχει οριστεί έξω από αυτό. Έξω από τα μπλοκ, δείχνει ότι ο ορισμός βρίσκεται σε άλλη μονάδα μετάφρασης (αρχείο).
Ο προσδιοριστής thread_local (_Thread_local πριν από τη C23), ο οποίος εισήχθη στο πρότυπο C11, χρησιμοποιείται για τη δήλωση μεταβλητών που είναι τοπικές σε ένα συγκεκριμένο νήμα εκτέλεσης.
Από την έκδοση C23, η λέξη-κλειδί auto μπορεί επίσης να χρησιμοποιηθεί για τη δήλωση μεταβλητών με εξαγωγή τύπου (type inference), παρόμοια με τη C++.
Προσδιοριστές τύπου
[Επεξεργασία | επεξεργασία κώδικα]Οι τύποι μπορούν να τροποποιηθούν με προσδιοριστές (type qualifiers) για να υποδηλώσουν ειδικές ιδιότητες. Ο προσδιοριστής const υποδηλώνει ότι μια τιμή δεν μεταβάλλεται μετά την αρχικοποίησή της. Ομοίως, ο constexpr (από τη C23) είναι μια ισχυρότερη μορφή του const, όπου η τιμή πρέπει να είναι γνωστή κατά το χρόνο μεταγλώττισης. Ο προσδιοριστής volatile υποδεικνύει στον μεταγλωττιστή ότι η τιμή μπορεί να αλλάξει από εξωτερικούς παράγοντες (π.χ. υλικό ή άλλα νήματα), εμποδίζοντας ανεπιθύμητες βελτιστοποιήσεις κατά την ανάγνωση ή εγγραφή της.
Ελλιπείς τύποι
[Επεξεργασία | επεξεργασία κώδικα]Ένας ελλιπής τύπος (incomplete type) είναι μια δομή (struct) ή ένωση (union) της οποίας τα μέλη δεν έχουν ακόμη οριστεί, ένας τύπος πίνακα του οποίου οι διαστάσεις δεν έχουν καθοριστεί, ή ο τύπος void. Τέτοιοι τύποι δεν μπορούν να δεσμεύσουν μνήμη (καθώς το μέγεθός τους είναι άγνωστο), αλλά μπορούν να χρησιμοποιηθούν για τον ορισμό δεικτών.
Χρησιμοποιούνται συχνά για την υλοποίηση αναδρομικών δομών ή για την απόκρυψη δεδομένων (data hiding):
struct Integer* pt; // Δήλωση δείκτη σε ελλιπή τύπο
Δείκτες
[Επεξεργασία | επεξεργασία κώδικα]Στη δήλωση μιας μεταβλητής, ο αστερίσκος (*) σημαίνει «δείκτης σε». Για παράδειγμα, η int* px ορίζει μια μεταβλητή px που είναι δείκτης σε ακέραιο. Μια τιμή δείκτη συνδέει δύο πληροφορίες: μια διεύθυνση μνήμης και έναν τύπο δεδομένων.
Αναφορά και Αποαναφορά
[Επεξεργασία | επεξεργασία κώδικα]Ο τελεστής & (διεύθυνση του) επιστρέφει τη διεύθυνση μνήμης ενός αντικειμένου. Ο αστερίσκος * πριν από το όνομα μιας μεταβλητής δείκτη (όταν δεν βρίσκεται σε δήλωση) αποαναφέρεται (dereference), επιτρέποντας την πρόσβαση στην τιμή που βρίσκεται στη διεύθυνση που δείχνει.
int a = 10;
int* p = &a; // Ο p δείχνει στη διεύθυνση του a
int b = *p; // Ο b παίρνει την τιμή 10 μέσω του p
Πίνακες
[Επεξεργασία | επεξεργασία κώδικα]Ορισμός πίνακα
[Επεξεργασία | επεξεργασία κώδικα]Οι πίνακες αποθηκεύουν διαδοχικά στοιχεία του ίδιου τύπου. Ο ακόλουθος κώδικας δηλώνει έναν πίνακα 100 στοιχείων τύπου int:
int a[100];
Εάν δηλωθεί καθολικά, το μέγεθος πρέπει να είναι σταθερά. Εάν δηλωθεί μέσα σε συνάρτηση, το μέγεθος μπορεί να είναι μια έκφραση (Variable Length Array - VLA). Ο αριθμός των στοιχείων μπορεί να υπολογιστεί με την έκφραση sizeof(a) / sizeof(a[0]).
Πρόσβαση σε στοιχεία
[Επεξεργασία | επεξεργασία κώδικα]Η πρόσβαση στα στοιχεία γίνεται με τη χρήση αγκυλών, με τον δείκτη (index) να ξεκινά από το 0.
a[0] = 5; // Ανάθεση τιμής στο πρώτο στοιχείο
int x = a[99]; // Πρόσβαση στο τελευταίο στοιχείο
Πρόσβαση σε στοιχεία
[Επεξεργασία | επεξεργασία κώδικα]Η κύρια μέθοδος πρόσβασης στα στοιχεία ενός πίνακα είναι ο τελεστής δείκτη (subscript operator). Για παράδειγμα, η έκφραση a[i] έχει πρόσβαση στο στοιχείο με δείκτη i. Η δεικτοδότηση των πινάκων ξεκινά από το 0. Καθώς η C δεν παρέχει αυτόματο έλεγχο ορίων (bounds checking), η χρήση δείκτη εκτός των ορίων του πίνακα οδηγεί σε απροσδιόριστη συμπεριφορά.
Λόγω της στενής σχέσης πινάκων και δεικτών, η διεύθυνση κάθε στοιχείου μπορεί να εκφραστεί και μέσω αριθμητικής δεικτών.
Επειδή η έκφραση a[i] είναι σημασιολογικά ισοδύναμη με την *(a + i), η οποία με τη σειρά της είναι ισοδύναμη με την *(i + a), η έκφραση μπορεί να γραφτεί και ως i[a], αν και αυτή η μορφή χρησιμοποιείται σπάνια.
Πίνακες μεταβλητού μεγέθους
[Επεξεργασία | επεξεργασία κώδικα]Το πρότυπο C99 εισήγαγε τους πίνακες μεταβλητού μεγέθους (Variable-Length Arrays - VLA) σε εμβέλεια μπλοκ. Το μέγεθος τέτοιων πινάκων καθορίζεται κατά το χρόνο εκτέλεσης. Από την έκδοση C11, η δυνατότητα αυτή είναι προαιρετική για τους μεταγλωττιστές.
int n = 20;
int a[n]; // Το n καθορίζεται στο runtime
a[3] = 10;
Πολυδιάστατοι πίνακες
[Επεξεργασία | επεξεργασία κώδικα]Η C υποστηρίζει πολυδιάστατους πίνακες, οι οποίοι αποθηκεύονται στη μνήμη κατά γραμμή (row-major order). Ένας δισδιάστατος πίνακας είναι ουσιαστικά ένας μονοδιάστατος πίνακας, του οποίου κάθε στοιχείο είναι επίσης ένας πίνακας.
int array2d[ROWS][COLUMNS];
// Πρόσβαση στο στοιχείο της 5ης γραμμής και 4ης στήλης:
int val = array2d[4][3];
Ένας πολυδιάστατος πίνακας καταλαμβάνει μια συνεχή περιοχή της μνήμης και δεν πρέπει να συγχέεται με έναν πίνακα δεικτών προς πίνακες (Iliffe vector), όπου οι υπο-πίνακες μπορεί να βρίσκονται σε διαφορετικές θέσεις στη μνήμη.
Κείμενο
[Επεξεργασία | επεξεργασία κώδικα]Αν και η γλώσσα παρέχει τύπους για δεδομένα χαρακτήρων, δεν ορίζει έναν εγγενή τύπο δεδομένων για συμβολοσειρές (strings). Αντίθετα, χρησιμοποιείται η συμβολοσειρά τερματιζόμενη με μηδενικό (null-terminated string). Μια συμβολοσειρά είναι μια σειρά χαρακτήρων που καταλήγει στον χαρακτήρα \0 (μηδενική τιμή).
Συμβολοσειρές σταθερής τιμής (String literals)
[Επεξεργασία | επεξεργασία κώδικα]Μια συμβολοσειρά σταθερής τιμής περικλείεται σε διπλά εισαγωγικά, π.χ. "Hello world!". Ο μεταγλωττιστής προσθέτει αυτόματα τον μηδενικό χαρακτήρα τερματισμού στο τέλος.
Η γλώσσα υποστηρίζει τη συγχώνευση γειτονικών συμβολοσειρών κατά τη μεταγλώττιση, κάτι που επιτρέπει τον διαχωρισμό μεγάλων συμβολοσειρών σε πολλές γραμμές κώδικα:
printf("Hello " "world\n"); // Ισοδύναμο με "Hello world\n"
Σταθερές χαρακτήρων
[Επεξεργασία | επεξεργασία κώδικα]Η σταθερά χαρακτήρα (character constant) περικλείεται σε μονά εισαγωγικά, π.χ. 'A', και είναι τύπου int. Για να κατανοήσουμε τη διαφορά μεταξύ μιας σταθεράς συμβολοσειράς και μιας σταθεράς χαρακτήρα, σημειώστε ότι η "A" αποτελείται από δύο χαρακτήρες (τον 'A' και τον '\0'), ενώ η 'A' αντιπροσωπεύει έναν μόνο χαρακτήρα (την ακέραια τιμή 65 στο σύστημα ASCII).
Μια σταθερά χαρακτήρα δεν μπορεί να είναι κενή (η σύνταξη '' είναι μη έγκυρη). Υποστηρίζονται σταθερές πολλαπλών χαρακτήρων (π.χ. 'xy'), αλλά η χρήση τους δεν είναι φορητή καθώς η σειρά αποθήκευσής τους στο ακέραιο εξαρτάται από την υλοποίηση του μεταγλωττιστή.
Όπως και οι συμβολοσειρές, οι σταθερές χαρακτήρων μπορούν να δεχθούν προθέματα, όπως το L'A', το οποίο είναι τύπου wchar_t και αντιπροσωπεύει τον χαρακτήρα "A" σε κωδικοποίηση ευρέος χαρακτήρα.
Ακολουθίες διαφυγής (Backslash escapes)
[Επεξεργασία | επεξεργασία κώδικα]Οι χαρακτήρες ελέγχου δεν μπορούν να συμπεριληφθούν απευθείας σε μια συμβολοσειρά. Αντ' αυτού, κωδικοποιούνται μέσω μιας ακολουθίας διαφυγής που ξεκινά με μια ανάστροφη κάθετο (\).
Οι κυριότερες ακολουθίες διαφυγής περιλαμβάνουν:
| Ακολουθία | Σημασία |
|---|---|
\\ | Ανάστροφη κάθετος (Backslash) |
\" | Διπλά εισαγωγικά |
\' | Μονά εισαγωγικά |
\n | Νέα γραμμή (Newline) |
\r | Επαναφορά στην αρχή γραμμής (Carriage return) |
\b | Οπισθοδρόμηση (Backspace) |
\t | Οριζόντιος στηλοθέτης (Tab) |
\a | Ηχητική ειδοποίηση (Alert/Bell) |
\? | Ερωτηματικό (χρησιμοποιείται για την αποφυγή trigraphs) |
\OOO | Χαρακτήρας με οκταδική τιμή OOO |
\xhh | Χαρακτήρας με δεκαεξαδική τιμή hh |
\uhhhh | Unicode κωδικός (code point) κάτω από 10000 (από τη C99) |
Σημειώστε ότι στην πρότυπη συνάρτηση printf(), χρησιμοποιείται η ακολουθία %% για την εκτύπωση του χαρακτήρα του επί τοις εκατό (%).
Συμβολοσειρές ευρέων χαρακτήρων
[Επεξεργασία | επεξεργασία κώδικα]Καθώς ο τύπος char έχει μέγεθος 1 byte, μπορεί να αναπαραστήσει έως 255 διακριτούς χαρακτήρες, αριθμός που δεν επαρκεί για διεθνή σύνολα χαρακτήρων. Για την υποστήριξη αυτών, η C εισήγαγε τους ευρείς χαρακτήρες (wide characters), τύπου wchar_t, οι οποίοι γράφονται με το πρόθεμα L, π.χ. L"Γειά σου".
Το μέγεθος του wchar_t εξαρτάται από την υλοποίηση (συνήθως 2 ή 4 bytes). Στα συστήματα Microsoft Windows χρησιμοποιείται συνήθως η κωδικοποίηση UTF-16, ενώ στα συστήματα Unix προτιμάται η UTF-32.
Σήμερα, η γενικά προτεινόμενη μέθοδος υποστήριξης διεθνών χαρακτήρων είναι μέσω της κωδικοποίησης UTF-8, η οποία αποθηκεύεται σε απλούς πίνακες char και είναι συμβατή με το σύστημα ASCII.
Συμβολοσειρές μεταβλητού εύρους
[Επεξεργασία | επεξεργασία κώδικα]Μια κοινή εναλλακτική λύση αντί του wchar_t είναι η χρήση μιας κωδικοποίησης μεταβλητού εύρους, όπου ένας λογικός χαρακτήρας μπορεί να εκτείνεται σε πολλές θέσεις της συμβολοσειράς. Οι συμβολοσειρές αυτές μπορούν να ενσωματωθούν σε σταθερές τιμές είτε αυτούσιες (με κίνδυνο σύγχυσης του μεταγλωττιστή), είτε χρησιμοποιώντας αριθμητικές ακολουθίες διαφυγής (π.χ. "\xc3\xa9" για το χαρακτήρα "é" σε UTF-8). Η κωδικοποίηση UTF-8 σχεδιάστηκε ειδικά για συμβατότητα με τις συναρτήσεις της πρότυπης βιβλιοθήκης της C, καθώς δεν περιέχει ενσωματωμένους μηδενικούς χαρακτήρες (nulls) και επιτρέπει τον εύκολο επανασυγχρονισμό της ροής δεδομένων.
Δομές (Structures)
[Επεξεργασία | επεξεργασία κώδικα]Μια δομή (structure ή struct) είναι ένας περιέκτης που αποτελείται από μια ακολουθία ονομαστικών μελών διαφορετικών τύπων, παρόμοια με την εγγραφή (record) άλλων γλωσσών. Το πρώτο πεδίο ξεκινά από τη διεύθυνση της δομής και τα μέλη αποθηκεύονται σε διαδοχικές θέσεις στη μνήμη. Ωστόσο, ο μεταγλωττιστής μπορεί να εισάγει κενά πλήρωσης (padding) μεταξύ ή μετά τα μέλη για λόγους απόδοσης ή ευθυγράμμισης δεδομένων (alignment). Το μέγεθος μιας δομής περιλαμβάνει και αυτά τα κενά πλήρωσης.
Μια δομή δηλώνεται με τη λέξη-κλειδί struct, ακολουθούμενη από ένα προαιρετικό όνομα (ετικέτα) και το σώμα της δήλωσης μέσα σε άγκιστρα.
Το παρακάτω παράδειγμα δηλώνει μια δομή με το όνομα MyStruct που περιέχει τρία μέλη, καθώς και ένα στιγμιότυπο με το όνομα tee:
struct MyStruct {
int x;
float y;
char* z;
} tee;
Τα μέλη μιας δομής δεν μπορούν να έχουν ελλιπή τύπο ή τύπο συνάρτησης. Συνεπώς, ένα μέλος δεν μπορεί να είναι στιγμιότυπο της δομής που δηλώνεται εκείνη τη στιγμή (γιατί είναι ελλιπής τύπος), αλλά μπορεί να είναι δείκτης προς τον τύπο της δομής.
Αφού δηλωθεί ο τύπος, μπορούμε να δημιουργήσουμε νέα στιγμιότυπα:
struct MyStruct r;
Πολλοί προγραμματιστές χρησιμοποιούν τη λέξη-κλειδί typedef για να δημιουργήσουν ένα ψευδώνυμο (alias) της δομής, ώστε να μην χρειάζεται η λέξη struct σε κάθε δήλωση μεταβλητής:
typedef struct {
int i;
} Integer;
Integer n; // Χρήση του ψευδωνύμου αντί για struct ...
Πρόσβαση σε μέλη
[Επεξεργασία | επεξεργασία κώδικα]Η πρόσβαση σε ένα μέλος γίνεται με τη χρήση της τελείας (dot notation). Για παράδειγμα, χρησιμοποιώντας τη δήλωση της tee από παραπάνω, το μέλος y είναι προσβάσιμο ως tee.y.
Όταν η πρόσβαση στη δομή γίνεται μέσω δείκτη, χρησιμοποιείται ο τελεστής -> (βέλος). Αν έχουμε έναν δείκτη {{{1}}}, το μέλος y είναι προσβάσιμο ως ptee->y. Αυτό είναι συντακτική γλυκαντική (syntactic sugar) για την έκφραση (*ptee).y.
Ανάθεση και λειτουργίες
[Επεξεργασία | επεξεργασία κώδικα]Η ανάθεση τιμής σε ένα μέλος γίνεται όπως σε μια απλή μεταβλητή. Μια δομή μπορεί επίσης να ανατεθεί εξ ολοκλήρου σε μια άλλη δομή του ίδιου τύπου (αντιγραφή κατά τιμή), να περαστεί ως όρισμα σε συνάρτηση ή να επιστραφεί από αυτήν.
Η γλώσσα δεν υποστηρίζει την απευθείας σύγκριση δύο δομών (π.χ. {{{1}}}). Για τη σύγκριση απαιτείται κώδικας που ελέγχει κάθε πεδίο ξεχωριστά.
Πεδία bit (Bit fields)
[Επεξεργασία | επεξεργασία κώδικα]Η C επιτρέπει τον ορισμό μελών με συγκεκριμένο μέγεθος σε bit, γνωστά ως πεδία bit. Δηλώνονται ως μέλη τύπου int (signed/unsigned) ή _Bool, ακολουθούμενα από άνω-κάτω τελεία και τον αριθμό των bit.
Τα πεδία bit χρησιμοποιούνται συχνά για την εξοικονόμηση μνήμης ή τον έλεγχο υλικού (hardware registers). Δεν είναι δυνατή η λήψη της διεύθυνσής τους (τελεστής &) ούτε η χρήση του sizeof.
struct FlagStatus {
unsigned int flag : 1; // 1 bit (0 ή 1)
signed int num : 4; // 4 bit για αποθήκευση αριθμού
signed int : 3; // 3 bit κενό πλήρωσης (padding)
} g;
Χώροι ονομάτων (Namespaces)
[Επεξεργασία | επεξεργασία κώδικα]Η C δεν διαθέτει εγγενή υποστήριξη για χώρους ονομάτων (namespaces). Αυτό καθιστά τα ονόματα των συμβόλων επιρρεπή σε συγκρούσεις. Ωστόσο, είναι δυνατόν να προσομοιωθούν οι χώροι ονομάτων χρησιμοποιώντας στατικές (anonymous) δομές και δείκτες συναρτήσεων.
Παράδειγμα οργάνωσης βιβλιοθήκης Math:
// Math.h
const struct {
double PI;
double (*sin)(double);
} Math;
// Χρήση στον κώδικα:
double val = Math.sin(Math.PI / 2);
Ένωση (Union)
[Επεξεργασία | επεξεργασία κώδικα]Σε μεγάλο βαθμό, μια **ένωση** είναι παρόμοια με μια δομή, με τη διαφορά ότι τα πεδία της επικαλύπτονται στην ίδια θέση μνήμης. Αυτό επιτρέπει την αποθήκευση διαφορετικών τύπων δεδομένων, αλλά όχι ταυτόχρονα. Το μέγεθος μιας ένωσης είναι ίσο με το μέγεθος του μεγαλύτερου στοιχείου της συν τυχόν κενά πλήρωσης (padding).
Μια ένωση δηλώνεται με τη λέξη-κλειδί union. Το παρακάτω παράδειγμα δηλώνει την ένωση MyUnion:
union MyUnion {
int x;
float y;
char* z;
} n;
Αρχικοποίηση
[Επεξεργασία | επεξεργασία κώδικα]Βαθμωτοί τύποι (Scalars)
[Επεξεργασία | επεξεργασία κώδικα]Η αρχικοποίηση μιας μεταβλητής κατά τη δήλωσή της γίνεται με το σύμβολο της ισότητας. Λόγω της γραμματικής της γλώσσας, ένας βαθμωτός τύπος μπορεί να περικλείεται σε οποιονδήποτε αριθμό αγκυλών (αν και αυτό δεν συνηθίζεται).
int x = 12;
int y = { 23 }; // Έγκυρο αλλά σπάνιο
Λίστα αρχικοποίησης (Initializer list)
[Επεξεργασία | επεξεργασία κώδικα]Οι δομές, οι ενώσεις και οι πίνακες μπορούν να αρχικοποιηθούν μέσω μιας λίστας αρχικοποίησης. Εάν τα στοιχεία της λίστας είναι λιγότερα από τα μέλη της δομής, τα υπόλοιπα μέλη μηδενίζονται.
int a[10] = {}; // Όλα τα στοιχεία γίνονται 0
struct MyStruct s = { 3, 3.14, "Pi" };
Εάν ένας πίνακας δηλωθεί χωρίς ρητό μέγεθος, ο αριθμός των στοιχείων στη λίστα αρχικοποίησης καθορίζει το μέγεθος του πίνακα και συμπληρώνει τον τύπο του.
Καθορισμένοι αρχικοποιητές (Designated initializers)
[Επεξεργασία | επεξεργασία κώδικα]Από το πρότυπο C99, οι καθορισμένοι αρχικοποιητές επιτρέπουν την αρχικοποίηση μελών με βάση το όνομά τους, με οποιαδήποτε σειρά.
struct MyStruct pi = { .z = "Pi", .x = 3, .y = 3.1415 };
Αυτή η μέθοδος είναι ιδιαίτερα χρήσιμη σε ενώσεις, όπου μπορούμε να επιλέξουμε ποιο μέλος θα αρχικοποιηθεί (παλαιότερα αρχικοποιούνταν υποχρεωτικά μόνο το πρώτο μέλος):
union MyUnion value = { .y = 3.1415 };
Για πίνακες, μπορούμε να καθορίσουμε τη θέση (index) της αρχικοποίησης:
int a[10] = { [0] = 1, [5] = 8, [9] = 2 };
Σύνθετες σταθερές (Compound literals)
[Επεξεργασία | επεξεργασία κώδικα]Είναι δυνατή η χρήση της μεθοδολογίας αρχικοποίησης για τη δημιουργία σύνθετων σταθερών τιμών για δομές και πίνακες «εν πτήσει»:
// Δείκτης που δημιουργείται από μια σύνθετη σταθερά πίνακα
int* ptr = (int[]){ 10, 20, 30, 40 };
// Δείκτης σε πίνακα
float (*foo)[3] = &(float[]){ 0.5f, 1.f, -0.5f };
struct MyStruct pi = (struct MyStruct){ 3, 3.1415, "Pi" };
Οι σύνθετες σταθερές συχνά συνδυάζονται με καθορισμένους αρχικοποιητές για να κάνουν τη δήλωση πιο ευανάγνωστη:
pi = (struct MyStruct){ .z = "Pi", .x = 3, .y = 3.1415 };
Δείκτες συναρτήσεων
[Επεξεργασία | επεξεργασία κώδικα]Ένας δείκτης σε συνάρτηση δηλώνεται ως εξής:
Ο παρακάτω κώδικας επιδεικνύει τη χρήση δείκτη συνάρτησης για την επιλογή μεταξύ πρόσθεσης και αφαίρεσης:
Τελεστές
[Επεξεργασία | επεξεργασία κώδικα]Ροή ελέγχου
[Επεξεργασία | επεξεργασία κώδικα]Σύνθετη εντολή (Compound statement)
[Επεξεργασία | επεξεργασία κώδικα]Μια σύνθετη εντολή (ή μπλοκ εντολών) είναι ένα ζεύγος αγκυλών που περιέχει οποιονδήποτε αριθμό εντολών. Απαιτείται για το σώμα των συναρτήσεων και για δομές ελέγχου που περιλαμβάνουν περισσότερες από μία εντολές.
Εντολή If
[Επεξεργασία | επεξεργασία κώδικα]Η εντολή υπό συνθήκη if έχει την εξής μορφή:if (έκφραση) { εντολή } else { εντολή }
Εάν η έκφραση αξιολογηθεί ως μη μηδενική (αληθής), εκτελείται η πρώτη εντολή. Διαφορετικά, εκτελείται η δεύτερη (εφόσον υπάρχει το else). Κάθε else αντιστοιχεί στο πλησιέστερο προηγούμενο if που δεν έχει ήδη αντιστοιχιστεί.
if (i == 1) {
printf("Είναι 1");
} else if (i == 2) {
printf("Είναι 2");
} else {
printf("Είναι κάτι άλλο");
}
Εντολή Switch
[Επεξεργασία | επεξεργασία κώδικα]Η εντολή switch μεταφέρει τον έλεγχο στην ετικέτα case που αντιστοιχεί στην τιμή μιας ακέραιας έκφρασης. Εάν δεν βρεθεί αντιστοιχία, ο έλεγχος μεταφέρεται στην ετικέτα default (αν υπάρχει). Η εκτέλεση συνεχίζεται μέχρι να βρεθεί μια εντολή break ή μέχρι το τέλος του σώματος της switch.
Η σύνταξη έχει ως εξής:switch (έκφραση) { case τιμή: εντολές default: εντολές }
Εάν δεν χρησιμοποιηθεί η εντολή break, η εκτέλεση «ρέει» στην επόμενη περίπτωση (fall-through). Αυτό μπορεί να είναι χρήσιμο σε κάποιες περιπτώσεις, αλλά συχνά αποτελεί πηγή προγραμματιστικών λαθών.
Από το πρότυπο C2Y, είναι πλέον δυνατή η χρήση εύρους τιμών (case range) με την αποσιωπητική τελεία ....
switch (num) {
case 1 ... 3:
printf("Ο αριθμός είναι 1, 2 ή 3\n");
break;
default:
printf("Άλλος αριθμός\n");
}
Εντολές επανάληψης (Loops)
[Επεξεργασία | επεξεργασία κώδικα]Υπάρχουν τρεις μορφές εντολών επανάληψης στη C:
- while: Ο έλεγχος της συνθήκης γίνεται πριν από κάθε εκτέλεση του σώματος του βρόχου.
- do-while: Ο έλεγχος της συνθήκης γίνεται μετά από κάθε εκτέλεση, διασφαλίζοντας ότι ο βρόχος θα εκτελεστεί τουλάχιστον μία φορά.
- for: Συνδυάζει την αρχικοποίηση, τον έλεγχο και την ενημέρωση σε μία γραμμή.
// Παράδειγμα while
while (i < 10) {
i++;
}
// Παράδειγμα for (από τη C99 επιτρέπεται η δήλωση μέσα στο for)
for (int i = 0; i < 10; ++i) {
printf("%d ", i);
}
Η λογική της for μπορεί να αναπαρασταθεί με μια while ως εξής:
αρχικοποίηση;
while (συνθήκη) {
εντολές;
ενημέρωση;
}
Αν και η C δεν διαθέτει εγγενή βρόχο foreach (όπως η Java ή η C++), αυτό μπορεί να προσομοιωθεί χρησιμοποιώντας μακροεντολές (macros) ή την κλασική δομή for πάνω σε πίνακες.
Εντολές άλματος (Jump statements)
[Επεξεργασία | επεξεργασία κώδικα]Υπάρχουν τέσσερις εντολές άλματος (που μεταφέρουν τον έλεγχο άνευ όρων): η goto, η continue, η break, και η return.
Η εντολή goto μεταβιβάζει τον έλεγχο του προγράμματος σε μια εντολή που φέρει ετικέτα. Η σύνταξή της είναι:goto όνομα-ετικέτας;
Η εντολή continue μεταφέρει τον έλεγχο στο σημείο συνέχισης του βρόχου της εσωτερικότερης εντολής επανάληψης που την περιέχει. Πρέπει να βρίσκεται υποχρεωτικά εντός βρόχου.
while (true) {
// ...
continue; // Μεταπηδά στον έλεγχο της συνθήκης
}
Η εντολή break τερματίζει την εκτέλεση μιας εντολής for, while, do ή switch. Ο έλεγχος περνά στην εντολή που ακολουθεί αμέσως μετά το κλείσιμο της δομής ελέγχου.
Η εντολή return επιστρέφει τον έλεγχο στη συνάρτηση που έκανε την κλήση. Εάν ακολουθείται από μια έκφραση, η τιμή αυτής επιστρέφεται ως αποτέλεσμα της συνάρτησης.
Ετικέτες (Labels)
[Επεξεργασία | επεξεργασία κώδικα]Μια ετικέτα ορίζει ένα σημείο στον κώδικα στο οποίο μπορεί να μεταφερθεί ο έλεγχος. Αποτελείται από ένα αναγνωριστικό που ακολουθείται από άνω-κάτω τελεία.
if (i == 1) {
goto END;
}
// υπόλοιπος κώδικας
END:
printf("Τέλος προγράμματος");
Από το πρότυπο C2Y, η C υποστηρίζει πλέον **ονομαστικούς βρόχους** (labelled loops), παρόμοια με τη Java. Αυτό επιτρέπει τη σύνδεση ετικετών με εντολές for και τη χρήση των break και continue για έξοδο από πολλαπλά επίπεδα βρόχων (multi-level breaks).
outer:
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (condition) {
break outer; // Έξοδος και από τους δύο βρόχους ταυτόχρονα
}
}
}
Συναρτήσεις
[Επεξεργασία | επεξεργασία κώδικα]Ορισμός
[Επεξεργασία | επεξεργασία κώδικα]Για μια συνάρτηση που επιστρέφει τιμή, ο ορισμός αποτελείται από το όνομα του τύπου επιστροφής, το όνομα της συνάρτησης (το οποίο πρέπει να είναι μοναδικό), μια λίστα παραμέτρων σε παρενθέσεις και ένα μπλοκ εντολών που περιλαμβάνει την εντολή return.
Η σύνταξη είναι η εξής:
Μια συνάρτηση που δεν επιστρέφει τιμή δηλώνεται με τη λέξη-κλειδί void στη θέση του τύπου επιστροφής. Το πρότυπο της C δεν περιλαμβάνει λάμδα (lambda) συναρτήσεις, αν και ορισμένοι μεταγλωττιστές τις προσφέρουν ως επέκταση.
Παράμετροι
[Επεξεργασία | επεξεργασία κώδικα]Η λίστα παραμέτρων είναι μια λίστα δηλώσεων μεταβλητών διαχωρισμένων με κόμμα. Ο τύπος επιστροφής δεν μπορεί να είναι πίνακας ή συνάρτηση, αλλά μπορεί να είναι δείκτης σε αυτά.
int f()[3]; // Σφάλμα: συνάρτηση που επιστρέφει πίνακα
int (*g())[3]; // Σωστό: συνάρτηση που επιστρέφει δείκτη σε πίνακα
Εάν μια συνάρτηση δεν δέχεται παραμέτρους, η λίστα μπορεί να είναι κενή ή να περιέχει τη λέξη void. Η χρήση του void θεωρείται βέλτιστη πρακτική, καθώς υποδηλώνει ρητά ότι η συνάρτηση δεν δέχεται ορίσματα.
Μια συνάρτηση μπορεί να δέχεται μεταβλητό αριθμό ορισμάτων χρησιμοποιώντας αποσιωπητικά ... στο τέλος της λίστας (όπως η printf). Ο χειρισμός αυτών των ορισμάτων γίνεται μέσω της βιβλιοθήκης <stdarg.h>.
Κλήση συναρτήσεων
[Επεξεργασία | επεξεργασία κώδικα]Για να χρησιμοποιηθεί μια συνάρτηση βιβλιοθήκης, πρέπει πρώτα να δηλωθεί, συνήθως μέσω της οδηγίας #include για το αντίστοιχο αρχείο επικεφαλίδας (.h). Η σύνδεση (linking) με τον κώδικα της βιβλιοθήκης γίνεται κατά το στάδιο της σύνδεσης του προγράμματος.
Μεταβίβαση ορισμάτων
[Επεξεργασία | επεξεργασία κώδικα]Στη C, τα ορίσματα μεταβιβάζονται πάντα κατά τιμή (pass by value), πράγμα που σημαίνει ότι η συνάρτηση λαμβάνει ένα αντίγραφο της τιμής και δεν μπορεί να τροποποιήσει την αρχική μεταβλητή. Για να τροποποιηθεί μια μεταβλητή από μια συνάρτηση, ο προγραμματιστής πρέπει να μεταβιβάσει τη διεύθυνση της μεταβλητής (δείκτη), προσομοιώνοντας τη μεταβίβαση κατ' αναφορά (pass by reference).
Παράδειγμα τροποποίησης μεταβλητής μέσω δείκτη:
void incInt(int* y) {
(*y)++; // Αυξάνει την τιμή στη διεύθυνση που δείχνει ο y
}
int main(void) {
int x = 7;
incInt(&x); // Μεταβιβάζει τη διεύθυνση του x
return 0;
}
Μεταβίβαση πινάκων
[Επεξεργασία | επεξεργασία κώδικα]Όταν ένας πίνακας περνάει ως παράμετρος σε μια συνάρτηση, μετατρέπεται αυτόματα σε δείκτη προς το πρώτο του στοιχείο. Επομένως, οι αλλαγές στα στοιχεία του πίνακα μέσα στη συνάρτηση επηρεάζουν τον αρχικό πίνακα.
void setArray(int array[], int index) {
array[index] = 123;
}
Ο παραπάνω ορισμός είναι ισοδύναμος με τον: void setArray(int* array, int index).
Από το πρότυπο C99, μπορεί να χρησιμοποιηθεί η λέξη-κλειδί static μέσα στις αγκύλες της παραμέτρου, π.χ. int array[static 4], για να δηλωθεί ότι ο πίνακας πρέπει να έχει τουλάχιστον ένα συγκεκριμένο μέγεθος.
Attributes (Γνωρίσματα)
[Επεξεργασία | επεξεργασία κώδικα]Τα **attributes** (γνωρίσματα) προστέθηκαν στο πρότυπο C23 (προερχόμενα από τη C++11) και επιτρέπουν τη χρήση ακολουθιών προσδιορισμού γνωρισμάτων. Τα attributes μπορούν να εφαρμοστούν σε οποιοδήποτε σύμβολο τα υποστηρίζει, όπως συναρτήσεις και μεταβλητές, ώστε ο μεταγλωττιστής να χειριστεί το αντίστοιχο σύμβολο με ειδικό τρόπο.
Μπορούν να θεωρηθούν παρόμοια με τα annotations της Java, καθώς παρέχουν πρόσθετες πληροφορίες στον μεταγλωττιστή, αλλά διαφέρουν στο ότι δεν είναι μεταδεδομένα προσβάσιμα μέσω reflection. Επίσης, στη C δεν είναι δυνατή η δημιουργία προσαρμοσμένων (custom) attributes από τον χρήστη, αν και υπάρχουν attributes ειδικά για κάθε κατασκευαστή (vendor-specific), τα οποία συνήθως συνοδεύονται από έναν χώρο ονομάτων (π.χ. τα [[gnu::*]] για τους GCC και Clang).
Η σύνταξη για τη χρήση ενός attribute σε μια συνάρτηση είναι η εξής:
[[nodiscard]]
bool satisfiesProperty(const struct MyStruct* s);
Το πρότυπο ορίζει τα ακόλουθα τυπικά attributes:
| Όνομα | Περιγραφή |
|---|---|
[[noreturn]] |
Υποδηλώνει ότι η συγκεκριμένη συνάρτηση δεν επιστρέφει ποτέ στον καλούντα (π.χ. τερματίζει το πρόγραμμα). |
[[deprecated]][[deprecated("reason")]] |
Υποδηλώνει ότι η χρήση του συμβόλου επιτρέπεται αλλά αποθαρρύνεται για τον λόγο που καθορίζεται. |
[[fallthrough]] |
Υποδηλώνει ότι η ροή (fall through) από μια ετικέτα case στην επόμενη είναι εσκεμμένη. |
[[maybe_unused]] |
Καταστέλλει τις προειδοποιήσεις (warnings) του μεταγλωττιστή για μια οντότητα που δεν χρησιμοποιείται. |
[[nodiscard]][[nodiscard("reason")]] |
Προκαλεί προειδοποίηση εάν η τιμή επιστροφής μιας συνάρτησης αγνοηθεί από τον χρήστη. |
[[unsequenced]] |
Υποδηλώνει ότι μια συνάρτηση είναι χωρίς κατάσταση (stateless), χωρίς παρενέργειες, idempotent και ανεξάρτητη. |
[[reproducible]] |
Υποδηλώνει ότι μια συνάρτηση είναι χωρίς παρενέργειες και idempotent. |
Παραπομπές
[Επεξεργασία | επεξεργασία κώδικα]- ↑ «C | Definition, History, Applications, & Facts | Britannica». Encyclopedia Britannica (στα Αγγλικά). Αρχειοθετήθηκε από το πρωτότυπο στις 11 Σεπτεμβρίου 2025. Ανακτήθηκε στις 3 Φεβρουαρίου 2026.