Προεπεξεργαστής C. Προγραμματισμός Ι 1

Σχετικά έγγραφα
Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120)

Προεπεξεργαστής της C. C Preprocessor. Προγραμματισμός II 1

Μεταγλώττιση και σύνδεση πολλαπλών αρχείων κώδικα. Προγραμματισμός II 1

(programming interfaceή/και application programming interface API).

make Προγραμματισμός II 1

Προγραμματισμός Ι (ΗΥ120)

Βιβλιοθήκες Αφηρημένοι τύποι δεδομένων. Προγραμματισμός II 1

Προγραμματισμός Ι. Προεπεξεργαστής. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

CE121 Προγραµµατισµός 2. Εισαγωγή σε Makefiles. CE121 -

#include <stdlib.h> Α. [-128,127] Β. [-127,128] Γ. [-128,128]

Προγραμματισμός Ι (HY120)

Α. unsigned int Β. double. Γ. int. unsigned char x = 1; x = x + x ; x = x * x ; x = x ^ x ; printf("%u\n", x); Β. unsigned char

CE121 Προγραµµατισµός 2. Εισαγωγή σε Makefiles. CE121 -

Ανάπτυξη Μεγάλων Εφαρµογών στη Γλώσσα C (2)

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι. Πολλαπλά Αρχεία. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120)

Δομημένος Προγραμματισμός

Η γλώσσα προγραμματισμού C

Ανάπτυξη και Σχεδίαση Λογισμικού

Αλγόριθμοι Ταξινόμησης Μέρος 1

Προγραμματισμό για ΗΜΥ

Προγραμματισμός Η/Υ (ΤΛ2007 )

Η γλώσσα προγραμματισμού C

double sum(double a, double b) { return(a+b); } double my_avg(double a, double b) { return(sum(a, b)/2.0); }

Η-Υ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ. Εργαστήριο 1 Εισαγωγή στη C. Σοφία Μπαλτζή s.mpaltzi@di.uoa.gr

Δυναμική δέσμευση και αποδέσμευση μνήμης. Προγραμματισμός II 1

Ενότητες στην C Τεχνική Υλοποίησης Αφαιρετικών Τύπων Δεδομένων στην C

Αρχεία Κεφαλίδων ΑΡΧΕΙΑ ΚΕΦΑΛΙΔΩΝ. Γεώργιος Παπαϊωάννου ( )

Προγραμματισμός Ι (ΗΥ120)

int a[5]; a[0] a[1] a[2] a[3] a[4] 15/10/2009

Προγραμματισμός Υπολογιστών με C++

Αντικειµενοστρεφής Προγραµµατισµός

Διάλεξη 11: Οργάνωση Προγραμμάτων σε Πολλαπλά Αρχεία

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120)

που θα δώσει αποτέλεσµα 48, λόγω της αριστερής προσεταιριστικότητας των τελεστών / και *, ενώ η επιθυµητή αντικατάσταση θα ήταν η

ΕΡΓΑΣΤΗΡΙΟ 1 ΕΙΣΑΓΩΓΗ ΣΤΗ C. Τµήµα Πληροφορικής και Τηλεπικοινωνιών

Περιεχόμενα. Πρόλογος... 17

Περιεχόμενα. Πρόλογος... 21

Η γλώσσα προγραμματισμού C

Προγραμματισμός Ι (HY120)

Προγραµµατισµός Ι (ΗΥ120)

Δομημένος Προγραμματισμός (ΤΛ1006)

ΑΦAΙΡΕΤΙΚΟΣ (ή ΑΦΗΡΗΜΕΝΟΣ) ΤΥΠΟΣ ΔΕΔΟΜΕΝΩΝ (ΑΤΔ) (Abstract Data Type-ADT) - σύνολο δεδομένων (data, objects) - σύνολο πράξεων στα δεδομένα

Προγραμματισμός Ι (ΗΥ120)

Ενδεικτική περιγραφή μαθήματος

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός σε C. Πράξεις με bits (bitwise operators)

Διάλεξη 2η: Αλγόριθμοι και Προγράμματα

Κεφάλαιο : Εισαγωγή στην C: (Διαλέξεις 3-4)

ΤΥΠΟΣ ΔΕΔΟΜΕΝΩΝ (ΑΤΔ) (Abstract Data Type-ADT)

Α Β Γ static; printf("%c\n", putchar( A +1)+2); B DB BD. int i = 0; while (++i); printf("*");

Α' Εξάμηνο ΕΙΣΑΓΩΓΗ ΣΤΟ ΔΟΜΗΜΕΝΟ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ. Ασκήσεις Επανάληψης

Εισαγωγή στον Προγραμματισμό

ΕΡΓΑΣΤΗΡΙΟ 6: Συναρτήσεις και Αναδρομή

ΕΠΛ232 Προγραμματιστικές Τεχνικές και Εργαλεία Οργάνωση Προγραμμάτων σε Πολλαπλά Αρχεία (Κεφάλαιο , KNK-2ED)

Λειτουργικά Συστήματα

Διαδικασία Ανάπτυξης Λογισμικού

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος

Διαδικαστικός Προγραμματισμός

Προγραμματισμός H/Y Ενότητα 5: Συναρτήσεις. Επικ. Καθηγητής Συνδουκάς Δημήτριος Τμήμα Διοίκησης Επιχειρήσεων (Γρεβενά)

ΔΟΜΗΜΕΝΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Προγραμματισμός ΙI (E)

Διδάσκων: Δημήτριος Βαρσάμης

ΕΙΣΑΓΩΓΗ ΣΤΟN ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΠΟΛΥΤΕΧΝΙΚΗ ΣΧΟΛΗ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ Η/Υ ΚΑΙ ΠΛΗΡΟΦΟΡΙΚΗΣ

ΑΡΧΕΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ

Δομημένος Προγραμματισμός (ΤΛ1006)

ΑΡΧΕΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ

Διαδικασιακός Προγραμματισμός

Π. Σταθοπούλου ή Οµάδα Α (Φοιτητές µε µονό αριθµό Μητρώου ) ιδασκαλία : Παρασκευή 11πµ-13µµ ΗΛ7

Εισαγωγή στον Προγραµµατισµό. Πανεπιστήµιο Θεσσαλίας Τµήµα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ

ΣΧΕΔΙΑΣΗ ΚΑΙ ΥΛΟΠΟΙΗΣΗ ΛΟΓΙΣΜΙΚΟΥ

Ανάπτυξη και Σχεδίαση Λογισμικού

Διαδικασιακός Προγραμματισμός

Δημιουργία & Τερματισμός Διεργασιών. Προγραμματισμός II 1

Εισαγωγή στην Πληροφορική

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

Δομημένος Προγραμματισμός

Εργαστήριο Λειτουργικών Συστημάτων. Minix Overview

Τμήμα Πληροφορικής & Επικοινωνιών Δρ. Θεόδωρος Γ. Λάντζος

Σε γενικές γραμμές, είναι καλή πρακτική να γράϕουμε προγράμματα C που αποτελούνται από πολλές και μικρές συναρτήσεις, παρά από λίγες και μεγάλες.

Π. Σταθοπούλου ή Οµάδα Α (Φοιτητές µε µονό αριθµό Μητρώου ) ιδασκαλία : Παρασκευή 11πµ-13µµ ΗΛ7

Επεξεργασία Αρχείων Κειµένου

Προγραμματισμός Η/Υ (ΤΛ2007 )

Προγραμματισμός Συστημάτων

B. Ενσωμάτωση Ιθαγενών Μεθόδων

Οι συναρτήσεις στη γλώσσα C

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ II. Υφαντόπουλος Νικόλαος Υποψήφιος Διδάκτορας Contact:

Δομημένος Προγραμματισμός

Ανάπτυξη και Σχεδίαση Λογισμικού

Δομημένος Προγραμματισμός (ΤΛ1006)

ΕΙΣΑΓΩΓΗ ΣΤΟΝ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ Ενδεικτικές Απαντήσεις Εξετάσεων Β' Περιόδου Θέµα 1. (α')

Α' Εξάμηνο ΕΙΣΑΓΩΓΗ ΣΤΟ ΔΟΜΗΜΕΝΟ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ

Προγραµµατισµός Ι Εισαγωγή Πανεπιστήµιο Πελοποννήσου Τµήµα Πληροφορικής & Τηλεπικοινωνιών Προγραµµατισµός Ι Νικόλαος Δ. Τσελίκας

Transcript:

Προεπεξεργαστής C Προγραμματισμός Ι lalis@inf.uth.gr 1

Βασική ιδέα Ο προεπεξεργαστής (pre-proccesor) της C είναι ένα πρόγραμμα που εκτελείται και μετασχηματίζει τον πηγαίο κώδικα πριν αυτός δοθεί στον μεταγλωτιστή για μετάφραση (gcc -E). Πραγματοποιεί αλλαγές σε επίπεδο κειμένου, με την μορφή «απλών» αντικαταστάσεων χαρακτήρων, χωρίς γνώση ή ερμηνεία του συντακτικού της C. Οι πιο χαρακτηριστικές χρήσεις: μακροεντολές εισαγωγή κειμένου υπό συνθήκη αντιγραφή κειμένου από άλλα αρχεία Προγραμματισμός Ι lalis@inf.uth.gr 2

P1x preproc P1 x compiler x -> y P1 y CPU y Προγραμματισμός Ι lalis@inf.uth.gr 3

Μακροεντολές #define <macro> <expr> Ορίζει μια μακροεντολή <macro> προς χρήση στο κείμενο του προγράμματος, που θα αντικατασταθεί από τον προεπεξεργαστή με την έκφραση <expr>. Η επεξεργασία των macros γίνεται με απλή αντικατάσταση σε επίπεδο κειμένου. Σημείωση: χρειάζεται προσοχή (παρενθέσεις) έτσι ώστε να αποφεύγονται λάθη με τελεστές που ακολουθούν στο κείμενο του προγράμματος. Σημείωση2: χρειάζεται προσοχή με «παραμέτρους» των macros καθώς αυτές αποτιμώνται εκ νέου, όσες φορές εμφανίζονται στην έκφραση. Προγραμματισμός Ι lalis@inf.uth.gr 4

/* ορισμός συμβολικών σταθερών */ #define N 100 int t[n]; for (i=0; i < N; i++ ) { if (i == N) { Προγραμματισμός Ι lalis@inf.uth.gr 5

/* ορισμός συμβολικών σταθερών */ #define N 10*10 int t[n]; for (i=0; i < N; i++ ) { if (i == N) { Προγραμματισμός Ι lalis@inf.uth.gr 6

/* ορισμός συμβολικών σταθερών */ #define A 10 #define B 20 #define C (A+B) #define D (C*Β) Προγραμματισμός Ι lalis@inf.uth.gr 7

/* ορισμός μακροεντολών */ #define LOWERCASE(a) (a- 'A' + 'a') int main(int argc, char *argv[]) { char c; do { c=getchar(); if ((c >= 'A') && (c <= 'Z')) { c=lowercase(c); putchar(c); while (c!= '/n'); Προγραμματισμός Ι lalis@inf.uth.gr 8

/* ορισμός μακροεντολών */ #define LOWERCASE(a) (((a>='a') && (a<='z'))? a-'a'+'a' : a) int main(int argc, char *argv[]) { char c; do { c=getchar(); putchar(lowercase(c)); while (c!= '/n'); Προγραμματισμός Ι lalis@inf.uth.gr 9

/* μη χρήση παρενθέσεων */ #define PLUSONE(x) x+1 int main(int argc, char *argv[]) { int i,j; scanf("%d",&i); j=plusone(i)*2; printf("%d\n",j); Προγραμματισμός Ι lalis@inf.uth.gr 10

/* και πιο σωστά */ #define PLUSONE(x) (x+1) int main(int argc, char *argv[]) { int i,j; scanf("%d",&i); j=plusone(i)*2; printf("%d\n",j); Προγραμματισμός Ι lalis@inf.uth.gr 11

/* επανειλημμένη αποτίμηση παραμέτρων... */ #define SQUARE(a) ((a)*(a)) int main(int argc, char *argv[]) { int i,j; scanf("%d",&i); j=square(++i); printf("%d\n",j); Προγραμματισμός Ι lalis@inf.uth.gr 12

Εισαγωγή υπό συνθήκη #if, #else, #elif, #endif #ifdef, #ifndef Συνθήκες πάνω σε συμβολικά ονόματα. Αν μια συνθήκη ισχύει τότε το αντίστοιχο κείμενο συμπεριλαμβάνεται για μετάφραση, διαφορετικά όχι. Το κείμενο που συμπεριλαμβάνεται / αφαιρείται μπορεί να είναι οτιδήποτε (δεν χρειάζεται να αντιστοιχεί σε κάποια συντακτική ενότητα). Χρήσιμο για επιλογή ανάμεσα σε διαφορετικές εκδόσεις του κώδικα (κώδικας αποσφαλμάτωσης, υποστήριξη για διαφορετικές πλατφόρμες κλπ). Χρησιμοποιείται και για την αποφυγή διπλής εισαγωγής κειμένου (βλέπε #include). Προγραμματισμός Ι lalis@inf.uth.gr 13

#define DEBUG #ifdef DEBUG /* ή #if defined(debug) */ /* my debugging code */ #endif Προγραμματισμός Ι lalis@inf.uth.gr 14

#define DEBUG aaaaaaaaa #ifdef DEBUG bbbbbbbbb #endif aaaaaaaaa bbbbbbbbb ccccccccc ccccccccc Προγραμματισμός Ι lalis@inf.uth.gr 15

#define DEBUG #ifdef DEBUG /* ή #if defined(debug) */ /* my debugging code */ #endif Προγραμματισμός Ι lalis@inf.uth.gr 16

#define DEBUG aaaaaaaaa #ifdef DEBUG bbbbbbbbb #endif aaaaaaaaa ccccccccc ccccccccc Προγραμματισμός Ι lalis@inf.uth.gr 17

#ifdef OLDVERSION /* old code */ #else /* new code */ #endif Προγραμματισμός Ι lalis@inf.uth.gr 18

#define LINUX_VERSION 5 #if LINUX_VERSION == 6 /* code for this platform */ #else /* code for default platform */ #endif Προγραμματισμός Ι lalis@inf.uth.gr 19

#ifndef INCL_X /* ή #if!defined(incl_x) */ #define INCL_X /* avoid recursive inclusion */ /* my code */ #endif Προγραμματισμός Ι lalis@inf.uth.gr 20

Εισαγωγή αρχείων #include "filename" Τα περιεχόμενα του αρχείου filename εισάγονται ακριβώς στο σημείο που εμφανίζεται η εντολή. Αν το αρχείο προς εισαγωγή δεν βρίσκεται στον κατάλογο εργασίας, τότε ο αντίστοιχος κατάλογος πρέπει να προσδιορίζεται με το όνομα του αρχείου. Αν το όνομα του αρχείου δίνεται σε < > αντί " ", τότε ο προεπεξεργαστής θεωρεί ότι το αρχείο βρίσκεται στον κατάλογο όπου έχουν τοποθετηθεί τα header files της C, π.χ. /usr/include. Αν το αρχείο που εισάγεται περιέχει και αυτό με την σειρά του εντολές #include τότε γίνεται εισαγωγή και αυτών των αρχείων μέσα στο αρχείο. Προγραμματισμός Ι lalis@inf.uth.gr 21

/* αρχείο stdio.h */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa /* αρχείο myprog */ #include <stdio.h> bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb /* αρχείο myprog */ /* αρχείο stadio.h */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb Προγραμματισμός Ι lalis@inf.uth.gr 22

/* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa /* αρχείο file2 */ #include "file1" bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb /* αρχείο file2 */ /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb Προγραμματισμός Ι lalis@inf.uth.gr 23

/* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa /* αρχείο file2 */ #include "file1" bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb /* αρχείο file3 */ #include "file2" cccccccccc cccccccccc cccccccccc /* αρχείο file3 */ /* αρχείο file2 */ /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa bbbbbbbbbb bbbbbbbbbb bbbbbbbbbb cccccccccc cccccccccc cccccccccc Προγραμματισμός Ι lalis@inf.uth.gr 24

/* myprog_min.c */ int min(int a, int b) { if (a < b) { return(a); else { return(b); /* myprog_max.c */ int max(int a, int b) { if (a < b) { return(b); else { return(a); /* myprog_main.c */ #include "myprog_min.c" #include "myprog_max.c int main(int argc, char *argv[])) { int a,b; scanf("%d %d",&a,&b); printf("%d %d\n",min(a,b),max(a.b)); Προγραμματισμός Ι lalis@inf.uth.gr 25

Αποφυγή επανειλημμένης εισαγωγής Ένα αρχείο μπορεί να εισάγει με την σειρά του (έμμεσα) επιπλέον αρχεία, τα οποία μπορεί να εισάγονται ξανά από το κυρίως πρόγραμμα. Η διπλή εισαγωγή μπορεί να είναι ανεπιθύμητη. Π.χ. διπλή εισαγωγή μεταβλητών και συναρτήσεων που οδηγεί (στη μετάφραση) σε συντακτικά λάθη. Λύση 1: ο προγραμματιστής πρέπει να γνωρίζει ποιά αρχεία εισάγουν (έμμεσα) τα αρχεία που εισάγει στο πρόγραμμα του (άμεσα) ιδιαίτερα άκομψο. Λύση 2: το ίδιο το αρχείο φροντίζει να μην μπορεί να εισαχθεί δύο φορές στο ίδιο κείμενο κλασική περίπτωση χρήσης του #ifndef. Προγραμματισμός Ι lalis@inf.uth.gr 26

/* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa /* αρχείο file2 */ #include "file1" bbbbbbbbbb bbbbbbbbbb /* αρχείο file3 */ #include "file2" #include "file1" cccccccccc cccccccccc /* αρχείο file3 */ /* αρχείο file2 */ /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa bbbbbbbbbb bbbbbbbbbb /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa cccccccccc cccccccccc Προγραμματισμός Ι lalis@inf.uth.gr 27

#ifndef INC_FILE1 #define INC_FILE1 /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa #endif /* αρχείο file2 */ #include "file1" bbbbbbbbbb bbbbbbbbbb /* αρχείο file3 */ #include "file2" #include "file1" /* αρχείο file3 */ /* αρχείο file2 */ /* αρχείο file1 */ aaaaaaaaaa aaaaaaaaaa bbbbbbbbbb bbbbbbbbbb cccccccccc cccccccccc cccccccccc cccccccccc Προγραμματισμός Ι lalis@inf.uth.gr 28

/* myprog_min.c */ #ifndef INCL_MYPROGMIN #define INCL_MYPROGMIN int min(int a, int b) { #endif /* myprog_max.c */ #include "myprog_min.c" int max(int a, int b) { if (a == min(a,b)) { return(b); else { return(a); /* myprog_main.c */ #include "myprog_min.c" #include "myprog_max.c" int main(int argc, char *argv[])) { int a,b; scanf("%d %d",&a,&b); printf("%d %d\n",min(a,b),max(a.b)); Προγραμματισμός Ι lalis@inf.uth.gr 29

Ξεχωριστή μετάφραση Προγραμματισμός Ι lalis@inf.uth.gr 30

Επαναχρησιμοποίηση κώδικα Το μεγαλύτερο ίσως «στοίχημα» στην βιομηχανία λογισμικού είναι η επαναχρησιμοποίηση κώδικα. Ιδανικά, δεν χρειάζεται να ξαναγράψουμε κώδικα που έχει ήδη γραφτεί (από κάποιους άλλους). Πως χρησιμοπούμε κώδικα που υπάρχει; Προσέγγιση Α: αντιγράφουμε τον πηγαίο κώδικα, κάνοντας ίσως προσαρμογές για τις ανάγκες μας. Προσέγγιση Β: καλούμε τον εκτελέσιμο κώδικα, περνώντας τις παραμέτρους που χρειάζονται σύμφωνα με τις οδηγίες χρήσης / περιγραφή λειτουργικότητας του. Το Β απαιτεί μηχανισμό σύνδεσης κώδικα που έχει ήδη μεταφραστεί με τον κώδικα που γράφουμε. Προγραμματισμός Ι lalis@inf.uth.gr 31

P1x compiler x -> y P1 y P2x compiler x -> y P2 y P1 y P2 y linker P y CPU y Προγραμματισμός Ι lalis@inf.uth.gr 32

P1 P linker P1 P P2 P linker P2 P P3 P linker P3 P Προγραμματισμός Ι lalis@inf.uth.gr 33

P1 linker P1 P P2 linker P2 P P P3 linker P3 P Προγραμματισμός Ι lalis@inf.uth.gr 34

Ανάπτυξη αυτόνομων τμημάτων λογισμικού Σχεδίαση προγραμματιστικής διεπαφής: αποφασίζουμε το πως θα χρησιμοποιηθεί (προγραμματιστικά) η λειτουργικότητα. Κατασκευή header file: ορισμός της διεπαφής με την μορφή ενός header file που θα χρησιμοποιηθεί (κυρίως) από τα προγράμματα «πελάτες». Κατασκευή υλοποίησης: σε ξεχωριστό αρχείο υλοποιούμε τη λειτουργικότητα, όπως ορίζεται στο header file, που μεταφράζεται για να δημιουργηθεί το τμήμα κώδικα που θα συνδεθεί (αργότερα) με τον κώδικα από τα προγράμματα «πελάτες». Προγραμματισμός Ι lalis@inf.uth.gr 35

Header files στην C Η διεπαφή προγραμματισμού στην C ορίζεται μέσω ενός header file, που περιέχει (α) ορισμούς τύπων δεδομένων, (β) δηλώσεις σταθερών και καθολικών μεταβλητών, και (γ) δηλώσεις συναρτήσεων. Οι δηλώσεις μεταβλητών και συναρτήσεων πρέπει να έχουν τον προσδιορισμό extern, υποδεικνύοντας στον μεταγλωτιστή ότι οι αντίστοιχες υλοποιήσεις τους δεν βρίσκονται απαραίτητα στο ίδιο αρχείο. Κάνοντας #include ένα header file, το πρόγραμμα μπορεί να χρησιμοποιεί τύπους, μεταβλητές και συναρτήσεις που υλοποιούνται σε ξεχωριστό τμήμα λογισμικού (τον πηγαίο κώδικα του οποίου μπορεί να μην γνωρίζουμε) και να μεταφραστεί επιτυχώς. Προγραμματισμός Ι lalis@inf.uth.gr 36

Σύνδεση Όταν ένα πρόγραμμα μεταφράζεται με βάση τις δηλώσεις που περιέχει ένα header file, ο κώδικας που παράγεται από τον μεταφραστή περιέχει αναφορές σε εξωτερικές μεταβλητές και συναρτήσεις. Για να παραχθεί ο τελικός εκτελέσιμος κώδικας, απαιτείται μια επιπλέον διαδικασία ενσωμάτωσης των ορισμών και υλοποιήσεων τους, που βρίσκονται σε κάποιο άλλο αρχείο που έχει ήδη μεταφραστεί. Αυτή η διαδικασία ονομάζεται σύνδεση (linking). Αφού ο μεταφρασμένος κώδικας συνδεθεί με όλα τα υπόλοιπα μεταφρασμένα τμήματα που παρέχουν τους ορισμούς / υλοποιούν των εξωτερικών μεταβλητών / συναρτήσεων, δημιουργείται το τελικό εκτελέσιμο. Προγραμματισμός Ι lalis@inf.uth.gr 37

/* foo.h */ extern int i; extern void boo(); υλοποιείται από χρησιμοποιείται από /* foo.c */ #include "foo.h" int i=0; /* main.c */ #include "foo.h" int main(int argc, int *argv[]) { void boo() { i++; boo(); i++; boo(); Προγραμματισμός Ι lalis@inf.uth.gr 38

/* foo.h */ extern int i; extern void boo(); /* foo.c */ #include "foo.h" int i=0; void boo() { i++; /* foo.c */ #include /* foo.h foo.h */ extern int i; extern void boo(); int i=0; void boo() { i++; /* foo.o */ ------------ i -> 0 boo -> 5 ------------ 0 1 2 3 4 5 6 7 8 > gcc foo.c c o foo.o > Προγραμματισμός Ι lalis@inf.uth.gr 39

/* foo.h */ extern int i; /* main.c */ #include "foo.h" extern void boo(); /* foo.h */ /* main.c */ #include "foo.h" int main( ) { boo(); i++; boo(); extern int i; extern void boo(); int main( ) { boo(); i++; boo(); /* main.o */ ------------ i ->??? boo ->??? ------------ 0 boo 1 i 2 3 i 4 boo > gcc main.c c o main.o > Προγραμματισμός Ι lalis@inf.uth.gr 40

/* main.o */ ------------ i ->??? boo ->??? ------------ 0 boo 1 i 2 3 i 4 boo + /* foo.o */ ------------ i -> 0 boo -> 5 ------------ 0 1 2 3 4 5 6 7 8 /* test */ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 > gcc main.o foo.o o test > Προγραμματισμός Ι lalis@inf.uth.gr 41

Περισσότερα για το extern Ο προσδιορισμός extern χρησιμοποιείται για τις δηλώσεις εξωτερικών μεταβλητών / συναρτήσεων, που ορίζονται / υλοποιούνται σε ένα άλλο αρχείο. Η παράλειψη του extern σε δήλωση συνάρτησης (που δεν υλοποιείται τοπικά) οδηγεί σε αναζήτηση για «ταιριαστή» υλοποίηση της συνάρτησης στα αρχεία με τα οποία γίνεται σύνδεση του κώδικα. Η παράλειψη του extern σε δήλωση (καθολικής) μεταβλητής δεν εγγυάται τοπικότητα αν μια άλλη καθολική μεταβλητή δηλώνεται σε άλλο αρχείο με το ίδιο όνομα, κατά την διασύνδεση οι αναφορές σε αυτό το όνομα θα αφορούν την ίδια θέση μνήμης. Προγραμματισμός Ι lalis@inf.uth.gr 42

/* a.c */ int i; void incval() { i++; /* b.c */ extern void incval(); int main(int argc, char *argv[]) { extern int i; i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 43

/* a.c */ int i; void incval() { i++; /* b.c */ extern void incval(); int main(int argc, char *argv[]) { int i; i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 15 > Προγραμματισμός Ι lalis@inf.uth.gr 44

/* a.c */ int i; void incval() { i++; /* b.c */ extern int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 45

/* a.c */ extern int i; void incval() { i++; /* b.c */ int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 46

/* a.c */ extern int i; void incval() { i++; /* b.c */ extern int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test undefined reference to _i > Προγραμματισμός Ι lalis@inf.uth.gr 47

όμως... Προγραμματισμός Ι lalis@inf.uth.gr 48

/* a.c */ int i; void incval() { i++; /* b.c */ int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 49

/* a.c */ int i; void incval() { i++; /* b.c */ int i; void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 50

/* a.c */ int i; void incval() { i++; /* b.c */ int i; int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test >./test 15 16 > Προγραμματισμός Ι lalis@inf.uth.gr 51

Περισσότερα για το static Πως αποφεύγουμε μια καθολική μεταβλητή ή/και συνάρτηση που υλοποιούμε αποκλειστικά για τους σκοπούς του τοπικού κώδικα σε ένα αρχείο να «προσπελασθεί» (κατά λάθος) μέσα από κώδικα που βρίσκεται σε άλλο αρχείο; Με τον προσδιορισμό static επιτυγχάνεται η επιθυμητή τοπικότητα, δηλαδή η εμβέλεια των καθολικών μεταβλητών / συναρτήσεων περιορίζεται στο αρχείο όπου αυτές δηλώνονται / υλοποιούνται. Είναι αδύνατο να γίνει αναφορά σε αυτές (είτε επίτηδες είτε κατά λάθος) μέσα από κώδικα που βρίσκεται σε ένα άλλο αρχείο. Προγραμματισμός Ι lalis@inf.uth.gr 52

/* a.c */ int i; void incval() { i++; /* b.c */ static int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 15 > Προγραμματισμός Ι lalis@inf.uth.gr 53

/* a.c */ static int i; void incval() { i++; /* b.c */ int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test > >./test 15 15 > Προγραμματισμός Ι lalis@inf.uth.gr 54

/* a.c */ static int i; void incval() { i++; /* b.c */ extern int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test undefined reference to _i > Προγραμματισμός Ι lalis@inf.uth.gr 55

/* a.c */ int i; static void incval() { i++; /* b.c */ extern int i; extern void incval(); int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test undefined reference to _incval > Προγραμματισμός Ι lalis@inf.uth.gr 56

/* a.c */ int i; static void incval() { i++; /* b.c */ extern int i; int main(int argc, char *argv[]) { i=15; printf("%d\n",i); incval(); printf("%d\n",i); > gcc a.c c o a.o > gcc b.c c o b.o > gcc a.o b.o o test undefined reference to _incval > Προγραμματισμός Ι lalis@inf.uth.gr 57

Ένα «μικρό» πρόβλημα Όταν ένας κώδικας Α ορίζει μεταβλητές και υλοποιεί συναρτήσεις με σκοπό αυτές να χρησιμοποιηθούν μέσα από οποιοδήποτε άλλο κώδικα, τότε αυτές δεν μπορεί να δηλωθούν ως static. Όταν ο κώδικας Α συνδεθεί με ένα άλλο κώδικα Β, οι μεταβλητές / συναρτήσεις του Α είναι διαθέσιμες για σύνδεση με αντίστοιχες (άμεσες ή έμμεσες) εξωτερικές δηλώσεις που υπάρχουν στον Β. Αν οι εξωτερικές δηλώσεις του Β έχουν προκύψει από λάθος (π.χ. παράλειψη προσδιορισμού static σε δήλωση καθολικής μεταβλητής / συνάρτησης ή παράλειψη δήλωσης και υλοποίησης συνάρτησης), η διασύνδεση θα οδηγήσει σε λάθος αποτέλεσμα. Προγραμματισμός Ι lalis@inf.uth.gr 58

Η ρίζα του προβλήματος Αναφορά σε εξωτερικές μεταβλητές / συναρτήσεις γίνεται με βάση έναν επίπεδο χώρο ονομάτων, όπου δεν μπορεί να αποκλεισθεί η τυχαία χρήση του ίδιου ονόματος από διαφορετικά τμήματα κώδικα. Ο προγραμματιστής δεν έχει τη δυνατότητα να προσδιορίσει το τμήμα λογισμικού το οποίο θα πρέπει να παρέχει τον ορισμό / υλοποίηση μιας εξωτερικής μεταβλητής / συνάρτησης. Άλλες γλώσσες λύνουν το πρόβλημα δίνοντας σε κάθε τμήμα λογισμικού ένα μοναδικό όνομα με βάση το οποίο άλλα τμήματα κώδικα μπορεί να αναφέρονται (χωρίς πιθανότητα λάθους) σε μεταβλητές και συναρτήσεις που αυτό προσφέρει. Προγραμματισμός Ι lalis@inf.uth.gr 59