Η ΓΛΩΣΣΑ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ C: ΑΣΚΗΣΕΙΣ



Σχετικά έγγραφα
int array[10]; double arr[5]; char pin[20]; Προγραµµατισµός Ι

Εισαγωγή στην C. Μορφή Προγράµµατος σε γλώσσα C

ΘΕΜΑΤΑ ΕΞΕΤΑΣΗΣ ΚΑΙ ΑΠΑΝΤΗΣΕΙΣ

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

Εισαγωγή στον Προγραμματισμό (με. τη C)

scanf() scanf() stdin scanf() printf() int float double %lf float

ΘΕΜΑΤΑ ΕΞΕΤΑΣΗΣ ΚΑΙ ΑΠΑΝΤΗΣΕΙΣ ΤΟΥΣ

Κεφάλαιο , 3.2: Συναρτήσεις II. ( ιάλεξη 12) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

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

C: Από τη Θεωρία στην Εφαρµογή 2 ο Κεφάλαιο

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

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

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

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

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Εισαγωγή στον Προγραµµατισµό. Διάλεξη 2 η : Βασικές Έννοιες της γλώσσας προγραµµατισµού C Χειµερινό Εξάµηνο 2011

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

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

ΗΥ-150. Προγραµµατισµός. Εντολές Ελέγχου Ροής

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

Ενώσεις δεδομένων Απαριθμητές Ψηφιακοί τελεστές Αναδρομικές συναρτήσεις

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

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

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

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

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

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

Γλώσσα Προγραμματισμού C. Προγραμματισμός HY: Γλώσσα Προγραμματισμού C. Γρήγορος Πίνακας Αναφοράς Σύνταξης. Εισήγηση #4. Επαναληπτικές δομές:

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

Κεφάλαιο 8.7. Πίνακες & Συναρτήσεις ( ιάλεξη 17) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Διάλεξη 3η: Τύποι Μεταβλητών, Τελεστές, Είσοδος/Έξοδος

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

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

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

Κεφάλαιο Αλφαριθµητικές Σειρές Χαρακτήρων (Strings)

Κεφάλαιο , 3.2: Συναρτήσεις II. (Διάλεξη 12)

Προγραμματισμός Ι. Χαρακτήρες. Πανεπιστήμιο Πελοποννήσου Τμήμα Πληροφορικής & Τηλεπικοινωνιών

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

Πίνακες. 1 Πίνακες. 30 Μαρτίου 2014

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

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

Μεθόδων Επίλυσης Προβλημάτων

Ορισμός μεταβλητών δεικτών και αρχικοποίηση

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

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

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

3.1 Αριθμητικοί και Λογικοί Τελεστές, Μετατροπές Τύπου (Casting)

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

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

Μεθόδων Επίλυσης Προβλημάτων

a = 10; a = k; int a,b,c; a = b = c = 10;

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

Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης

ΑΣΚΗΣΗ 6: ΔΕΙΚΤΕΣ. Σκοπός της Άσκησης. 1. Εισαγωγικά στοιχεία για τους Δείκτες

Γλώσσα Προγραμματισμού C

for for for for( . */

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

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

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

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

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

ΑΣΚΗΣΗ 2: ΔΟΜΗ ΠΡΟΓΡΑΜΜΑΤΟΣ C, ΧΕΙΡΙΣΜΟΣ ΜΕΤΑΒΛΗΤΩΝ ΚΑΙ ΣΥΝΑΡΤΗΣΕΙΣ ΕΙΣΟΔΟΥ ΚΑΙ ΕΞΟΔΟΥ

Οι δείκτες στη γλώσσα C

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

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

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

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

ΑΣΚΗΣΗ 5: ΠΙΝΑΚΕΣ. Σχήµα 1: H έννοια των πινάκων

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

Κεφάλαιο 8.7. Πολυδιάστατοι Πίνακες ( ιάλεξη 18) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Πανεπιστήµιο Θεσσαλίας, THMMY HY120, Σεπτέµβριος 2015 ΟΝΟΜΑΤΕΠΩΝΥΜΟ:

Στην ενότητα αυτή θα µελετηθούν τα εξής επιµέρους θέµατα: ΕΠΛ 131 Αρχές Προγραµµατισµού I 3-2

Κεφάλαιο 8.7. Πολυδιάστατοι Πίνακες (Διάλεξη 19)

Προγραμματισμός Η/Υ Ι (Χρήση της C) 6 η Θεωρία ΜΟΝΟΔΙΑΣΤΑΤΟΙ ΠΙΝΑΚΕΣ

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

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

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ & ΥΠΟΛΟΓΙΣΤΙΚΗ ΦΥΣΙΚΗ ΕΞΕΤΑΣΗ IOYNIOY 2018 ΘΕΜΑΤΑ Α ΟΝΟΜΑΤΕΠΩΝΥΜΟ:... ΑΕΜ: ΕΞΑΜΗΝΟ:

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

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

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

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

ΠΛΗ111. Ανοιξη Μάθηµα 1 ο Ανασκόπηση της Γλώσσας Προγραµµατισµού C. Τµήµα Ηλεκτρονικών Μηχανικών και Μηχανικών Υπολογιστών Πολυτεχνείο Κρήτης

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ & ΥΠΟΛΟΓΙΣΤΙΚΗ ΦΥΣΙΚΗ

Μεθόδων Επίλυσης Προβλημάτων

Η Γλώσσα C Μία Σφαιρική Ανασκόπηση

Η γλώσσα προγραμματισμού C Οι συναρτήσεις στη C (2)

Υπολογισμός - Εντολές Ελέγχου

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

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

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

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

Προγραμματισμός Υπολογιστών & Υπολογιστική Φυσική

Διάλεξη 20: Χαμηλού Επιπέδου Προγραμματισμός II

Υπολογισμός - Εντολές Επανάληψης

Διάλεξη 5: Δείκτες και Συναρτήσεις

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

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

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

οµές (structures) Στην ενότητα αυτή θα µελετηθούν τα εξής επιµέρους θέµατα: Πίνακες δοµών, δείκτες σε δοµές, και αυτοαναφορικές δοµές.

Transcript:

ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΕΙΡΑΙΑ ΤΜΗΜΑ ΣΤΑΤΙΣΤΙΚΗΣ ΚΑΙ ΑΣΦΑΛΙΣΤΙΚΗΣ ΕΠΙΣΤΗΜΗΣ Η ΓΛΩΣΣΑ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ C: ΑΣΚΗΣΕΙΣ Για τους φοιτητές του µαθήµατος «Εισαγωγή στην Πληροφορική» Ε. Κοφίδης Ιούνιος 2005

Θα βρείτε εδώ έναν αριθµό ασκήσεων (µε και χωρίς απαντήσεις) που θα σας βοηθήσουν στη µελέτη του προγραµµατισµού σε C. Είναι ποικίλης δυσκολίας και περιλαµβάνουν από απλές ερωτήσεις έως ολοκληρωµένα προγράµµατα και βιβλιοθήκες συναρτήσεων. Συνιστάται να µελετηθούν όλες προσεκτικά και να γίνει προσπάθεια κατανόησης όλων των απαντήσεων. Αν είναι δυνατό, θα βοηθούσε και ο µεταγλωττισµός και η εκτέλεση των δοσµένων (και ζητούµενων) προγραµµάτων σε κάποιο προγραµµατιστικό περιβάλλον C. Καλή µελέτη! Άσκηση 1 Γράψτε το µικρότερο δυνατό πρόγραµµα C. Θα µπορούσαµε επίσης να παραλείψουµε και την εντολή return, χρησιµοποιώντας τον τύπο void µια και η main δεν θα επιστρέφει πλέον τίποτε: void É Άσκηση 2 Πέρα από την ορθότητα ενός κώδικα C, σηµαντικό ρόλο στη χρησιµότητά του παίζει και ο τρόπος γραφής του. Ξαναγράψτε τον παρακάτω κώδικα ώστε να είναι πιο ευανάγνωστος. int x,y;printf( \n ώστε δύο αριθµούς: );scanf( %d %d,&x,&y);printf( \n\nο %d είναι µεγαλύτερος.\n,(x>y)?x:y); int x,y; printf( \n ώστε δύο αριθµούς: ); scanf( %d %d,&x,&y); printf( \n\nο %d είναι µεγαλύτερος.\n,(x > y)? x : y); 1

É Άσκηση 3 a) Γράψτε µια εντολή if που να καταχωρεί την τιµή της µεταβλητής x στη µεταβλητή y αν η x έχει τιµή µεταξύ 1 και 20. ιαφορετικά, αφήστε την y αµετάβλητη. b) Χρησιµοποιείστε τον τελεστή συνθήκης για να κάνετε το ίδιο. a) if (x >= 1 && x <= 20) y = x; b) y = (x >= 1 && x <= 20)? x : y; Παρατήρηση: Οι παρενθέσεις στο b) δεν είναι απαραίτητες (γιατί;). É Άσκηση 4 Έστω ότι οι µεταβλητές x, y, z έχουν τιµές 1, 2, και 2, αντίστοιχα. Σε ποια τιµή υπολογίζεται καθεµιά από τις ακόλουθες εκφράσεις; α. (1 + 2 * 3) β. ((1 + 2) * 3) γ. 10 % 3 * 3 (1 + 2) δ. 10 % (3 * 3) (1 + 2) ε. (5 == 5) στ. x = y ζ. x == y η. x == y == z θ. x!= z ι. x = y = z ια. x = y!= z α. 7 β. 9 γ. 0 δ. 2 ε. 1 στ. 2 ζ. 0 η. 0 θ. 1 ι. 2 ια. 0 É Άσκηση 5 Έστω ότι οι µεταβλητές x, y, z έχουν τιµές 1, 3, και 4, αντίστοιχα. Για καθεµιά από τις παρακάτω εκφράσεις βρείτε αν είναι αληθής (Α) ή ψευδής (Ψ): α. x β. x + y γ. x + y == z δ. y % 2!= z % 2 ε.!(x + y == z) (y == z) στ. (z / x == z / y) (y > z && z < x) (x + y!= z) 2

Μια έκφραση στη C είναι αληθής αν έχει τιµή µη-µηδενική. Είναι ψευδής µόνο αν είναι µηδέν. α. Α β. Α γ. Α δ. Α ε. Ψ στ. Ψ É Άσκηση 6 Γράψτε συνάρτηση που να επιστρέφει 1 ή 0 ανάλογα µε το αν το ακέραιο όρισµά της είναι άρτιος ή περιττός (Υπόδειξη: Μπορείτε να χρησιµοποιήσετε τον bitwise τελεστή &). Ένας τρόπος να δει κανείς αν ένας ακέραιος είναι άρτιος ή περιττός είναι να εξετάσει αν το τελευταίο ψηφίο (bit) στη δυαδική του αναπαράσταση είναι 0 ή 1, αντίστοιχα Αυτό µπορεί να γίνει µε τη βοήθεια του τελεστή &. Υπενθυµίζουµε ότι ένα bit στη δυαδική αναπαράσταση του x & y είναι 1 αν και µόνο αν τα αντίστοιχα bits των x, y είναι και τα δύο 1. Για να ελέγξουµε εποµένως αν το τελευταίο bit του x είναι 0 ή 1, αρκεί να υπολογίσουµε το & του x µε τον αριθµό µε δυαδική αναπαράσταση 000...01, που είναι ο 1. Παρακάτω χρησιµοποιούµε την οκταδική αναπαράσταση του 1, που γράφεται προτάσσοντας ένα 0. iseven(int x) return(!(x & 01)); É Άσκηση 7 ιορθώστε τυχόν λάθη στον παρακάτω κώδικα. Μπορείτε να τον κάνετε πιο αποδοτικό; int x: printf( ώστε έναν ακέραιο αριθµό: ); scanf(%d, x); if (x = 1) printf(ο αριθµός είναι 1.\n); if (x!= 1) printf(ο αριθµός δεν είναι 1.\n); Τα λάθη και οι παραλείψεις σηµειώνονται παρακάτω µε κόκκινο χρώµα: int x; 3

printf( ώστε έναν ακέραιο αριθµό: ); scanf( %d, &x); if (x == 1) printf( Ο αριθµός είναι 1.\n ); else printf( Ο αριθµός δεν είναι 1.\n ); Παρατήρηση: Με τη βοήθεια της else αποφεύγεται ο επανέλεγχος της ισότητας της x µε το 1, που δεν είναι απαραίτητος.é Άσκηση 8 Γράψτε πρόγραµµα που να υπολογίζει το πλήθος των δευτερολέπτων ενός έτους. (Θεωρείστε ότι το έτος έχει 365 ηµέρες.) #define DAYS_PER_YR 365L #define HRS_PER_DAY 24L #define MINS_PER_HR 60L #define SECS_PER_MIN 60L printf( Ένα έτος έχει %ld δευτερόλεπτα.\n, DAYS_PER_YR * HRS_PER_DAY * MINS_PER_HR * SECS_PER_MIN); Η απάντηση είναι 31536000 δευτερόλεπτα, αριθµός πολύ µεγάλος για να χωρέσει σε int. Γι αυτό το λόγο, οι σταθερές που πολλαπλασιάζουµε ορίζονται να είναι τύπου long (µε την κατάληξη L) και η printf εµφανίζει το αποτέλεσµα ως τύπου long (µε το προσδιοριστικό µετατροπής ld). Επίσης, αν και οι σταθερές εδώ δεν υπάρχει περίπτωση ν αλλάξουν σε κάποια µεταγενέστερη έκδοση του κώδικα (µια ηµέρα θα έχει πάντα 24 ώρες, κ.ο.κ.), η χρήση συµβολικών σταθερών κάνει τον κώδικα πιο ευανάγνωστο και κατανοητό. É Άσκηση 9 Γράψτε πρόγραµµα που να διαβάζει δύο (το πολύ τριψήφιους) φυσικούς αριθµούς και να εµφανίζει στην οθόνη το άθροισµα και τη διαφορά τους. Τα αποτελέσµατα θα δίνονται όπως στο παράδειγµα: 124 124 +307-307 ------ ------ 431-183 4

short x, y; printf( ώστε δύο φυσικούς (το πολύ τριψήφιους) αριθµούς: ); scanf( %d %d, &x, &y); printf( %5d\t\t%5d\n +%3d\t\t -%3d\n------\t\t------ \n%5d\t\t%5d\n, x, x, y, y, x + y, x y); Παρατήρηση: εν έχουµε συµπεριλάβει έλεγχο ορθότητας των εισαχθέντων αριθµών. Συµπληρώστε τον κώδικα ώστε το πρόγραµµα να αποκρίνεται κατάλληλα σε περίπτωση που εισαχθεί αριθµός που δεν είναι φυσικός ή είναι τετραψήφιος και πάνω. Παρατηρήστε ότι χρησιµοποιήσαµε τον τύπο short, µια και οι ακέραιοι που περιµένουµε δεν παίρνουν µεγάλες τιµές. É Άσκηση 10 Γράψτε πρόγραµµα που να υπολογίζει την ενέργεια στην οποία θα µετασχηµατιζόταν µια 2 8 δοσµένη µάζα m σύµφωνα µε το γνωστό τύπο του Einstein, E = mc, όπου c = 3 10 m/s είναι η ταχύτητα του φωτός. /* ήλωση συναρτήσεων εισόδου/εξόδου */ #define LIGHTSPEED 3.0e08 /* συµβολική σταθερά για το c */ /* ορίζεται να επιστρέφει int */ float m; double E; /* πρέπει να είναι double ώστε να χωράει µεγάλες τιµές */ printf("αυτό το πρόγραµµα υλοποιεί τον τύπο του Einstein \ E=m*c^2.\n\n"); printf(" ώστε τη µάζα m (σε γραµµάρια):"); scanf("%f", &m); if (m < 0) fprintf(stderr, "Η µάζα δεν µπορεί να είναι αρνητική!\n"); return(1); /* επιστροφή κωδικού 1 όταν συνέβει λάθος */ E = m * LIGHTSPEED * LIGHTSPEED; printf("e=%g Joules\n", E); /* %g σηµαίνει απλή ή εκθετική µορφή,όποια καταλαµβάνει λιγότερο χώρο */ 5

/* επιστροφή κωδικού 0 όταν όλα πήγαν καλά */ Με χρήση συναρτήσεων και µακροεντολών, το παραπάνω πρόγραµµα θα µπορούσε να ξαναγραφτεί ως εξής: double einstein(float); /* δήλωση της συνάρτησης einstein */ float m; printf("αυτό το πρόγραµµα υλοποιεί τον τύπο του Einstein \ E=m*c^2.\n\n"); printf(" ώστε τη µάζα m (σε γραµµάρια):"); scanf( %f, &m); if (m < 0) fprintf(stderr, Η µάζα δεν µπορεί να είναι αρνητική!\n ); return(1); printf( E=%g\n, einstein(m)); /* Τα παρακάτω θα µπορούσαν να βρίσκονται σε διαφορετικό αρχείο */ #define LIGHTSPEED 3.0e08 /* µακροεντολή για τον υπολογισµό τετραγώνου */ #define sqr(x) ((x) * (x)) /* ορισµός συνάρτησης einstein */ double einstein(float m) return(m * sqr(lightspeed)); É Άσκηση 11 Να γραφτεί πρόγραµµα που να επιλύει µια εξίσωση πρώτου βαθµού της µορφής ax + b = 0, όπου οι σταθερές a, b δίνονται από το χρήστη. Να εξετάζονται όλες οι ειδικές περιπτώσεις. 6

float a, b; printf("επίλυση της εξίσωσης ax+b=0 ως προς x.\n\n"); printf(" ώστε το a:"); scanf("%f", &a); printf(" ώστε το b:"); scanf("%f", &b); if (a==0) if (b==0) printf("απειρία λύσεων.\n"); return(1); else printf(" εν έχει λύση!\n"); return(2); /* φτάνουµε εδώ µόνο αν το a δεν είναι µηδέν */ printf("λύση x=%g\n", -b / a); É Άσκηση 12 Γράψτε την ακόλουθη εντολή if-else µε χρήση του τελεστή συνθήκης: if (x > 0) s = 1; else if (x < 0) s = -1; else s = 0; s = x > 0? 1 : x < 0? 1 : 0; Για να γίνει πιο κατανοητή η παραπάνω έκφραση, µπορούµε να προσθέσουµε (δεν είναι απαραίτητο) παρενθέσεις: s = (x > 0)? 1 : ((x < 0)? 1 : 0); É Άσκηση 13 Ξαναγράψτε τον παρακάτω κώδικα χρησιµοποιώντας την εντολή switch. Υποθέστε ότι τα MONDAY, TUESDAY, κ.λπ. είναι συµβολικές σταθερές που έχουν οριστεί παραπάνω. if (day == MONDAY day == TUESDAY day == WEDNESDAY day == THURSDAY day == FRIDAY) 7

printf( Εργασία...\n ); else if (day == SATURDAY day == SUNDAY) printf( Αργία!\n ); else printf( Λάθος ηµέρα!\n ); switch (day) case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: printf( Εργασία...\n ); break; case SATURDAY: case SUNDAY: printf( Αργία!\n ); break; default: printf( Λάθος ηµέρα \n ); break; É Άσκηση 14 Να γραφτεί πρόγραµµα για τον υπολογισµό του παραγοντικού ενός δοσµένου φυσικού αριθµού n. Να χρησιµοποιηθεί συνάρτηση µε και χωρίς αναδροµή. double factorial(int); int n; /* double µια και το n! µπορεί να είναι πολύ µεγάλος αριθµός */ printf("υπολογίζει το n! για δοσµένο n.\n\n"); printf(" ώστε τον n:"); scanf("%d", &n); /* Ο n δεν µπορεί να είναι αρνητικός */ if (n < 0) fprintf(stderr, "Ο n δεν µπορεί να είναι αρνητικός!\n"); return(1); printf("n!=%.0g\n", factorial(n)); 8

/* Μη-αναδροµική υλοποίηση */ double factorial(int n) double fact = 1; register int i; for (i = 2; i <= n; i++) /*Τι αποτέλεσµα έχει αυτό για n=1 ή 2; */ fact *= i; return(fact); /* Αναδροµική υλοποίηση (Προφανώς δεν µπορούν να είναι και οι δύο αυτές συναρτήσεις στο ίδιο project) */ double factorial(int n) if (n == 0) return(1); /* Από σύµβαση ισχύει ότι 0!=1 */ else return(factorial(n - 1) * n); Παρατήρηση: Η αναδροµή (recursion) συχνά διευκολύνει τη συγγραφή συναρτήσεων, ιδιαίτερα όταν πρόκειται για υπολογισµό ποσότητας, εξαρτώµενης από µια παράµετρο, τέτοιας ώστε να µπορεί να υπολογιστεί ξεκάθαρα κι εύκολα από την ίδια ποσότητα για µικρότερη (ή µεγαλύτερη) τιµή της παραµέτρου. Για παράδειγµα, η τιµή της συνάρτησης παραγοντικού f ( n) = 1 2 L n = n! υπολογίζεται εύκολα από την τιµή της στο n 1: f ( n) = f ( n 1) n. ηλαδή η συνάρτηση χρησιµοποιεί τιµές του εαυτού της σε µικρότερα ορίσµατα. Ο κανόνας αυτός επαναλαµβάνεται: f ( n 1) = f ( n 2) ( n 1), κ.ο.κ. Γνωρίζοντας τον κανόνα που µας δίνει την f (n) από την f ( n 1) καθώς και την τιµή της συνάρτησης στην ελάχιστη δυνατή τιµή της παραµέτρου, δηλαδή f ( 0) = 1, µπορούµε να υπολογίσουµε την f (n) σε οποιαδήποτε τιµή της παραµέτρου n. Ας δούµε n και το παράδειγµα της συνάρτησης g ( n) = a, όπου a µη-µηδενική σταθερά και n φυσικός αριθµός. Για να υλοποιήσουµε αναδροµικά τη συνάρτηση χρειαζόµαστε: 1) έναν (απλό) κανόνα που να µας δίνει τη g (n) από τη g ( n 1) και 2) την τιµή της g(n) για n = 0. Είναι φανερό ότι 1) g ( n) = a g( n 1) και 2) g ( 0) = 1. Να πώς θα γράφαµε τότε τη συνάρτηση σε γλώσσα C: double dpow(float a, int n) if (n == 0) return(1); else return(a * dpow(a, n - 1)); 9

Εύκολα µπορούµε να συµπεριλάβουµε και αρνητικούς εκθέτες: double dpow(float a, int n) if (n == 0) return(1); else if (n > 0) return(a * dpow(a, n - 1)); else return(dpow(a, n + 1) / a); Η χρήση αναδροµής θα πρέπει παρόλα αυτά να γίνεται µε µέτρο και µόνο σε περιπτώσεις που ταιριάζει αυτός ο τρόπος κλήσης. Κάτι που πρέπει να γνωρίζουµε καλά για τις αναδροµικές συναρτήσεις είναι ότι, λόγω του ότι οι κλήσεις της συνάρτησης στον εαυτό της αποθηκεύονται προσωρινά σε µια ειδική περιοχή µνήµης (στοίβα stack) αναµένοντας τις επιστροφές από την τελευταία προς την πρώτη, µεγάλες τιµές των παραµέτρων αναδροµής (n στα παραπάνω παραδείγµατα) µπορούν να προκαλέσουν υπερχείλιση αυτής της µνήµης (stack overflow) και ακαριαίο τερµατισµό του προγράµµατος. οκιµάστε, π.χ. να υπολογίσετε µε την αναδροµική συνάρτηση factorial το παραγοντικό του 300. É Άσκηση 15 n Γράψτε συνάρτηση που να υπολογίζει το διωνυµικό συντελεστή = k Μπορείτε να χρησιµοποιήσετε τη συνάρτηση της προηγούµενης άσκησης. n!. k!( n k)! double binomial(int n, int k) return(factorial(n) / (factorial(k) * factorial(n k))); Φυσικά, πριν τον ορισµό της συνάρτησης αυτής θα πρέπει να υπάρχει ο ορισµός ή δήλωση της factorial. É Άσκηση 16 Τι κάνει το παρακάτω πρόγραµµα; Ξαναγράψτε το χωρίς να χρησιµοποιήσετε εντολή goto. int x = 5; 10

start: if (x--) printf( %d\n, x); goto start; Το πρόγραµµα εµφανίζει τη φθίνουσα ακολουθία 4 3 2 1 0, έναν αριθµό σε κάθε γραµµή. Εναλλακτικά µπορεί να γραφτεί µε τη βοήθεια της εντολής while, ως εξής: int x = 5; while (x--) printf( %d\n, x); Άσκηση 17 Ένας φυσικός αριθµός είναι πρώτος (prime) αν δεν διαιρείται από κανέναν άλλον αριθµό εκτός, φυσικά, από το 1 και τον εαυτό του. Για παράδειγµα οι αριθµοί 1, 2, 3, 5, 7 είναι πρώτοι, ενώ οι 4, 6, 8, 9 δεν είναι. Γράψτε ένα πρόγραµµα που να ελέγχει αν ένας αριθµός που εισάγεται από το χρήστη είναι πρώτος ή όχι. Το πρόγραµµα ελέγχει, για δοσµένο αριθµό N, αν υπάρχει διαιρέτης του µεταξύ των αριθµών 2, 3,..., Ν-1. unsigned N; register int i; /* είναι πρώτος µέχρι αποδείξεως του αντιθέτου */ char prime = 1; printf( ώστε έναν φυσικό αριθµό: ); scanf( %u, &N); for (i = 2; i < N; i++) if (N % i == 0) prime = 0; /* υπάρχει διαιρέτης, άρα δεν είναι πρώτος */ 11

break; /* δεν χρειάζεται να ψάξουµε κι άλλο */ /* προσέξτε τη χρήση του τελεστή?: */ printf( Ο αριθµός %u%s είναι πρώτος.\n, N, prime? : δεν ); Άσκηση 18 Τι θα εµφανίσει το παρακάτω πρόγραµµα; void set(void), pa(void); float avg(int, int); float a; int x, y; int x, y; x = y = 4; set(); a = avg(x, y); pa(); printf( a = %f\n, a); void set(void) x = 10; y = 20; void pa(void) a = 0; printf( x = %d y = %d\n, x, y); float avg(int k, int l) float average; average = (k + l) / 2.0; return(average); 12

Οι µεταβλητές οι τοπικές στη main υπερισχύουν των εξωτερικών µε τα ίδια ονόµατα. Συνεπώς, η a υπολογίζεται σε (4 + 4) / 2=4. Όµως, αφού η a είναι εξωτερική, µηδενίζεται µέσα στην pa κι εποµένως αυτή η τιµή (µηδέν) είναι που εµφανίζεται στην οθόνη. Επίσης, η pa θα εµφανίσει τις τιµές 10 και 20 για τις x και y αφού στο χώρο της συνάρτησης αυτά τα ονόµατα αναφέρονται στις εξωτερικές µεταβλητές και οι τελευταίες έχουν τεθεί σε 10 και 20 µέσα στη set. É Άσκηση 19 Ποιό είναι το µέγεθος σε bytes καθενός από τους ακόλουθους πίνακες; Υποθέστε ότι ένας int καταλαµβάνει 2 bytes κι ένας double 8 bytes. char a[] = E, x, a, m, p, l, e ; char b[] = Example ; int c[2][3]; double d[10][10][10]; Ο πίνακας a έχει 7 στοιχεία χαρακτήρες. εδοµένου ότι πάντα ένας χαρακτήρας καταλαµβάνει 1 byte, ο a έχει µέγεθος 7 bytes. Ο b διαφέρει από τον a στο ότι είναι αλφαριθµητικό, δηλαδή έχει κι ένα 8 ο στοιχείο, το χαρακτήρα τερµατισµού \0. Έχει εποµένως µέγεθος 8 bytes. Ο c έχει 6 στοιχεία µεγέθους 2 bytes το καθένα, άρα συνολικό µέγεθος 12 bytes. Ο πίνακας d έχει 1000 στοιχεία, καθένα από τα οποία καταλαµβάνει 8 bytes. Έχει συνεπώς µέγεθος 8000 bytes. É Άσκηση 20 Γράψτε συνάρτηση που να βρίσκει τον ελάχιστο ενός δοσµένου πλήθους ακεραίων αριθµών. Οι αριθµοί αποθηκεύονται σε πίνακα. Να γραφτεί και πρόγραµµα δοκιµής της συνάρτησης. 1 #define MAX_N 100 /* µέγιστο πλήθος αριθµών */ int min(int [], int); /* δήλωση συνάρτησης */ int nums[max_n], /* πίνακας αριθµών */ N; /* πλήθος αριθµών */ register int i; /* µετρητής */ printf("αυτό το πρόγραµµα βρίσκει τον ελάχιστο ενός \ δοσµένου πλήθους ακεραίων.\n\n"); printf("πόσοι είναι οι αριθµοί? (το πολύ %d) ", MAX_N); 1 Ένα τέτοιο πρόγραµµα, που σκοπό έχει τη δοκιµή µιας συνάρτησης, καλείται και πρόγραµµα οδηγός (driver). 13

scanf("%d", &N); if (N <= 0) fprintf(stderr, "Άντε γειά!\n"); return(1); else if (N > MAX_N) fprintf(stderr, "Είναι πάρα πολλοί!\n"); return(2); printf(" ώστε τους αριθµούς:\n"); for (i = 0; i < N; i++) printf("αριθµός %3d:", i + 1); scanf("%d", &nums[i]); printf("\nο ελάχιστος είναι: %d\n", min(nums, N)); /* ορισµός συνάρτησης εύρεσης ελαχίστου */ /* Παρατηρείστε ότι οι τυπικές παράµετροι (a,n) δεν έχουν απαραίτητα το ίδιο όνοµα µε τις αντίστοιχες πραγµατικές (nums,n) µια και οι πρώτες είναι τοπικές στη min() ενώ οι δεύτερες στη. Για τον ίδιο λόγο, η µεταβλητή i στην πρώτη συνάρτηση δεν έχει καµία σχέση µε την οµώνυµή της στη δεύτερη. */ int min(int a[], int n) int min_a; register int i; É min_a = a[0]; for (i = 1; i < n; i++) if (a[i] < min_a) min_a = a[i]; return(min_a); Άσκηση 21 Γράψτε πρόγραµµα που να υπολογίζει το ιστόγραµµα των γραµµάτων σ ένα κείµενο, δηλαδή πόσες φορές εµφανίζεται κάθε γράµµα σ αυτό. 14

#include <ctype.h> register int ch; int hist[26] = 0; /* αρχικοποίηση σε µηδέν */ printf( Βρίσκει το ιστόγραµµα των γραµµάτων σ ένα κείµενο που εισάγει ο χρήστης.\nεισάγετε το κείµενο (τέλος µε Ctrl+Z):\n\n ); while ((ch = getchar())!= EOF) if (!isalpha(ch)) continue; /* µας ενδιαφέρουν µόνο τα γράµµατα */ /* ανεξάρτητα από το αν είναι κεφαλαία ή µικρά */ ch = tolower(ch); hist[ch - 'a']++; /* η έκφραση ch - 'a' παίρνει ακέραιες τιµές µεταξύ 0 και 25 */ printf("\n Να το αποτέλεσµα:\n\n"); for (ch = 0; ch < 26; ch++) /* αν κάποιο γράµµα εµφανίζεται καµία ή περισσότερες της µιας φορές, τότε το γράφουµε στον πληθυντικό, π.χ. a s */ printf("%3d %c%s\n", hist[ch], ch + 'a', (hist[ch]!= 1)? "'s" : ""); Παρατηρήσεις: 1. Χρησιµοποιούνται συναρτήσεις βιβλιοθήκης που κάνουν έλεγχο και µετατροπή είδους χαρακτήρα. Αυτές δηλώνονται στο αρχείο-επικεφαλίδα <ctype.h>. 2. Σηµειώστε ότι µπορείτε να τρέξετε αυτό το πρόγραµµα και µε τη χρήση της επανακατεύθυνσης (του DOS ή του UNIX). Π.χ. αν θέλετε να υπολογίσετε το ιστόγραµµα για το περιεχόµενο του αρχείου text κι έχετε ονοµάσει το εκτελέσιµο πρόγραµµα letter_hist, µπορείτε απλά να γράψετε τη διαταγή letter_hist<text. Το αποτέλεσµα είναι να επανακατευθύνθεί η είσοδος της getchar() ώστε να µην είναι πια το πληκτρολόγιο (συµβολικό αρχείο stdin) αλλά το αρχείο text. 3. Αξίζει να προσέξετε τη χρήση του τελεστή συνθήκης στην τελευταία printf. É Άσκηση 22 Γράψτε συνάρτηση που να υπολογίζει το άθροισµα δύο δισδιάστατων πινάκων τύπου float, δοσµένου µεγέθους. Γράψτε και πρόγραµµα δοκιµής της. 15

#define NROWS 3 /* πλήθος γραµµών */ #define NCOLS 3 /* πλήθος στηλών */ float A[NROWS][NCOLS], B[NROWS][NCOLS], C[NROWS][NCOLS]; register int i, j; /* δήλωση (πρωτότυπο) συνάρτησης άθροισης */ void addmtx(float [][NCOLS], float [][NCOLS], float [][NCOLS], int); printf( Πρόσθεση πινάκων %d X %d\n, NROWS, NCOLS); printf( Πρώτος πίνακας:\n ); /* ανάγνωση πρώτου πίνακα */ for (i = 0; i < NROWS; i++) printf( Γραµµή %d\n, i); for (j = 0; j < NCOLS; j++) scanf("%f", &A[i][j]); printf( εύτερος πίνακας:\n ); /* ανάγνωση δεύτερου πίνακα */ for (i = 0; i < NROWS; i++) printf( Γραµµή %d\n, i); for (j = 0; j < NCOLS; j++) scanf("%f", &B[i][j]); /* άθροιση δοσµένων πινάκων */ addmtx(a, B, C, NCOLS); printf( Άθροισµα:\n ); /* εµ φάνιση αποτελέσµατος */ for (i = 0; i < NROWS; i++) for (j = 0; j < NCOLS; j++) printf("%6.2f%c", C[i][j], (j < NCOLS 1)? '\t' : '\n'); /* ορισµός συνάρτησης άθροισης */ void addmtx(float a[][ncols], float b[][ncols], float c[][ncols], int nrows) register int i, j; for (i = 0; i < nrows; i++) for (j = 0; j < NCOLS; j++) c[i][j] = a[i][j] + b[i][j]; 16

Παρατηρήσεις: 1. Οι διαστάσεις των πινάκων έχουν θεωρηθεί σταθερές. Θα µπορούσαµε να επιτρέπουµε στο χρήστη να καθορίζει το ακριβές τους µέγεθος. Οι πίνακες θα µπορούσαν τότε να ορίζονται όπως παραπάνω (δηλαδή ως arrays µε µέγιστες επιτρεπτές διαστάσεις) ή ως δείκτες σε δείκτες και να καταλαµβάνουν χώρο στη µνήµη δυναµικά (µε τη βοήθεια των συναρτήσεων malloc ή calloc). 2. Οι εντολές ανάγνωσης για τους δύο πίνακες που πρόκειται να αθροιστούν είναι ίδιες. Κάτι τέτοιο υπαγορεύει να γράψουµε συνάρτηση που να διαβάζει πίνακα δοσµένου µεγέθους. Το ίδιο θα µπορούσε να γίνει (για λόγους κοµψότητας του κώδικα) και για την εµφάνιση πίνακα στην οθόνη. 3. Παρατηρείστε στη δήλωση και τον ορισµό της συνάρτησης ότι το πλήθος των στηλών των πινάκων, σε αντίθεση µε το πλήθος των γραµµών που είναι παράµετρος, δίνεται ρητά. Για να δούµε γιατί κάτι τέτοιο είναι απαραίτητο, ας θυµηθούµε πώς αποθηκεύεται στη C ένας δισδιάστατος πίνακας: οι γραµµές του αποθηκεύονται σε µια µεγαλύτερη γραµµή, η µία µετά την άλλη. Η πληροφορία που παρέχεται στη συνάρτηση από το όνοµα του πίνακα που περνιέται ως όρισµα είναι µόνο το πού αρχίζει στη µνήµη αυτός ο χώρος. Απαιτείται να καθοριστεί (ως σταθερά κι όχι ως παράµετρος) και το πού τελειώνει µία γραµµή κι αρχίζει η επόµενη, δηλαδή το πλήθος των στηλών. Αλλιώς, πώς µπορεί να ξέρει ποιό είναι π.χ. το στοιχείο a[1][0]; 4. Η εµφάνιση του πίνακα αποτελέσµατος γίνεται στη µορφή που γράφουµε τους πίνακες και στο χαρτί, δηλαδή τη µια γραµµή κάτω από την άλλη. Αυτό επιτυγχάνεται µε το να γράφεται µεταξύ δύο στοιχείων ένας κενός χαρακτήρας ( \t στο παράδειγµα) εκτός αν πρόκειται για το τέλος µιας γραµµής οπότε γράφεται ο χαρακτήρας νέας γραµµής ( \n ). Ο έλεγχος αυτός γίνεται µε τον τελεστή συνθήκης. Άσκηση 23 Η παρακάτω συνάρτηση υποτίθεται ότι ανταλλάσσει τις τιµές των παραµέτρων της. Για παράδειγµα, αν οι µεταβλητές a, b έχουν τιµές 1, 2 αντίστοιχα, µετά την κλήση swap(a, b) θα έχουν τιµές 2, 1. Είναι πράγµατι αυτό το αποτέλεσµα; Αν όχι, ξαναγράψτε τη συνάρτηση ώστε να εκτελεί σωστά αυτή τη λειτουργία. void swap(int x, int y) int temp; temp = x; x = y; y = temp; Είναι γνωστό ότι τα ορίσµατα σε µια συνάρτηση C περνιούνται µε τιµή (by value) και οι παράµετροι της συνάρτησης είναι µεταβλητές τοπικές σ αυτή, δηλαδή οποιεσδήποτε αλλαγές γίνονται σ αυτές δεν φαίνονται εκτός της συνάρτησης. Αυτό σηµαίνει ότι στην κλήση swap(a, b) οι παράµετροι x, y θα πάρουν τις τιµές 1, 2, µέσα στη swap θα αλλάξουν τις τιµές τους σε 2, 1, αλλά οι a, b δεν θα επηρεαστούν απ αυτό, αφού η swap 17

ξέρει µόνο τις τιµές των ορισµάτων a, b, και τίποτε παραπάνω. Για να επιτευχθεί το swap των a, b, θα πρέπει η συνάρτηση να µάθει κάτι παραπάνω γι αυτές από τις τιµές τους µόνο. Αν ξέρει τις διευθύνσεις τους, τότε µπορεί να αλλάξει και τις τιµές τους, αφού θα ξέρει πού βρίσκονται στη µνήµη αυτές οι µεταβλητές. Άρα οι παράµετροι της συνάρτησης δεν θα είναι πλέον ακέραιες µεταβλητές αλλά µεταβλητές δείκτη σε ακέραιες µεταβλητές και η κλήση θα είναι κλήση µε αναφορά (by reference): void swap(int *x, int *y) int temp; temp = *x; *x = *y; *y = temp; Η κλήση της swap θα είναι τώρα swap(&a, &b). É Άσκηση 24 Τι εµφανίζει στην οθόνη το παρακάτω πρόγραµµα; int y = 1; void test(float [], int *, int); int x = 2, y = 3, z = 4; float a[3] = 0; test(a, &x, z); printf( %.1f %.1f %d %d %d\n, a[0], a[1], x, y, z); void test(float A[], int *w, int u) u++; *w += u; A[0] = *w / u; Η έξοδος θα είναι: 1.0 0.0 7 3 4 To a[0] υπολογίζεται στην test ως 7/5, που δίνει 1 αφού και ο διαιρετέος και ο διαιρέτης είναι ακέραιοι. Αν επιθυµούσαµε πραγµατική διαίρεση, θα έπρεπε να 18

γράψουµε A[0] = (float)*a / b;. Το στοιχείο 1 του πίνακα είναι µηδέν, αφού ο πίνακας έχει αρχικοποιηθεί σε µηδενικά και η συνάρτηση µετέβαλε µόνο το πρώτο του στοιχείο. Η τιµή της µεταβλητής x µεταβάλλεται στη συνάρτηση λόγω του ότι περνάµε τη διεύθυνσή της (call by reference). Έτσι, στο σώµα της συνάρτησης, *w είναι η x και η *w += u; αλλάζει το περιεχόµενο της x. Αντίθετα, η τιµή της u δεν επηρεάζει το περιεχόµενο της z, αφού την τελευταία την περνάµε στη συνάρτηση µε τιµή (call by value). Αξίζει να υπενθυµίσουµε ότι το όνοµα του πίνακα a είναι η διεύθυνση της αρχής του, εποµένως το A[0] στη συνάρτηση ισοδυναµεί µε a[0], γι αυτό και αλλάζει η τιµή του a[0]. Τέλος, η τιµή που εµφανίζεται για την y είναι η 3 κι όχι η 1. Αν και υπάρχει εξωτερική µεταβλητή µ αυτό το όνοµα και τιµή 1, µέσα στη main υπερισχύει η τοπική µεταβλητή µε το ίδιο όνοµα. É Άσκηση 25 Είναι σωστό το ακόλουθο απόσπασµα κώδικα; int a[10];... a += 2; Το όνοµα ενός πίνακα είναι δείκτης στο πρώτο στοιχείο του. Ο ορισµός int a[10]; δεσµεύει 10 διαδοχικές θέσεις ακεραίων στη µνήµη και συνδέει το όνοµα a µε τη διεύθυνση της πρώτης θέσης. εν έχουµε δικαίωµα ν αλλάξουµε τη διεύθυνση αυτή. Η εντολή a += 2;, αν ήταν επιτρεπτή, θα σήµαινε µετατόπιση ολόκληρου του πίνακα δυο θέσεις προς τα δεξιά. É Άσκηση 26 Υποθέστε ότι p είναι ένας δείκτης τύπου float ο οποίος δείχνει στη θέση µνήµης 1000. Έστω ότι ο τύπος float απαιτεί 4 bytes για την αποθήκευσή του. Ποια θα είναι η τιµή του p αφού εκτελεστεί η εντολή p = p + 5; Το πρώτο byte του float στον οποίο δείχνει αρχικά ο p έχει διεύθυνση 1000. Η εντολή καταχώρησης αυξάνει τον p κατά 5 θέσεις float. Η νέα του τιµή δεν είναι 1000+5=1005. Τώρα δείχνει στο πρώτο byte της θέσης float που βρίσκεται 5 θέσεις µετά την αρχική. εδοµένου ότι κάθε τέτοια θέση καταλαµβάνει 4 bytes, η νέα τιµή του p θα είναι 1000+5x4=1020. Αυτό το παράδειγµα µας δείχνει το γιατί χρειάζεται να ξέρουµε σε τι τύπου θέσεις δείχνει ένας δείκτης. Έτσι, αν ο p ήταν δείκτης τύπου int κι ο τύπος int απαιτούσε 2 bytes, η εντολή p = p + 5; θα έδινε τιµή 1000+5x2=1010. É Άσκηση 27 Τι θα εµφανίσει στην οθόνη το ακόλουθο απόσπασµα κώδικα; (Χρησιµοποιείστε το χαρακτήρα υπογράµµισης, _, για να δηλώσετε ένα κενό διάστηµα.) int a[] = 10, 23, 18, 5, -2; int *p; 19

p = a; printf( %5d, *(p + 2)); Θα εµφανιστεί το στοιχείο του πίνακα a που βρίσκεται 2 θέσεις µετά το πρώτο, έτσι ώστε να καταλαµβάνει τουλάχιστον 5 θέσεις, δηλαδή 3 κενά και µετά το 18: 18 É Άσκηση 28 Είναι σωστό το ακόλουθο απόσπασµα κώδικα; #define MAX MIN+100 #define MIN 10 Όχι, διότι ο ορισµός της συµβολικής σταθεράς MIN είναι µετά τον ορισµό της MAX που χρειάζεται αυτόν της MIN. É Άσκηση 29 Η C δεν διαθέτει τύπο για µιγαδικούς αριθµούς. ηµιουργείστε το δικό σας τύπο και χρησιµοποιείστε τον για να γράψετε συναρτήσεις για βασικές πράξεις µε µιγαδικούς: πραγµατικό µέρος, φανταστικό µέρος, µέτρο, συζυγής, πρόσθεση, αφαίρεση, πολλαπλασιασµός, διαίρεση. Ένας µιγαδικός αριθµός z = x + iy = ( x, y) δεν είναι παρά ένα διατεταγµένο ζεύγος πραγµατικών αριθµών, δηλαδή ένα σηµείο στο πραγµατικό επίπεδο. Τι πιο κατάλληλο εποµένως για τύπο µιγαδικών δεδοµένων από µια δοµή µε δύο πραγµατικά µέλη; ίνονται παρακάτω ο ορισµός του τύπου Complex και οι συναρτήσεις υλοποίησης των βασικών πράξεων. Υπενθυµίζεται ότι, για δύο µιγαδικούς αριθµούς z = x + iy, w = u + iv, έχουµε: Re( z) = x, Im( z) = y, z = x 2 + y 2, z = x iy, z + w = ( x + u) + i( y + v), z w = ( x u) + i( y v), z w = xu yv + i( xv + yu), z / w = zw typedef struct double real; /* πραγµατικό µέρος */ double imag; /* φανταστικό µέρος */ Complex; #define sqr(x) ((x) * (x)) /* τετράγωνο πραγµατικού αριθµού */ #include <math.h> /* περιέχει τη δήλωση της sqrt() */ /* πραγµατικό µέρος */ double Re(Complex z) w 2 20

return(z.real); /* φανταστικό µέρος */ double Im(Complex z) return(z.imag); /* µέτρο */ double cabs(complex z) return(sqrt(sqr(z.real)+sqr(z.imag))); /* συζυγής */ double conj(complex z) z.imag = -z.imag; return(z); /* µιγαδική πρόσθεση */ Complex cadd(complex z, Complex w) z.real += w.real; z.imag += w.imag; return(z); /* µιγαδική αφαίρεση */ Complex csubtr(complex z, Complex w) z.real -= w.real; z.imag -= w.imag; return(z); /* µιγαδικός πολλαπλασιασµός */ Complex cmult(complex z, Complex w) Complex q; q.real = z.real * w.real z.imag * w.imag; q.imag = z.real * w.imag + z.imag * w.real; return(q); /* µιγαδική διαίρεση */ Complex cdiv(complex z, Complex w) 21

Complex q; double absw2; q = cmult(z, conj(w)); absw2 = sqr(cabs(w)); q.real /= absw2; q.imag /= absw2; return(q); Ας σηµειωθεί ότι θα µπορούσαµε να χρησιµοποιήσουµε στις συναρτήσεις τις Re, Im, αντί των z.real, z.imag, κ.λπ. É Άσκηση 30 Γράψτε πρόγραµµα που θα µετράει τον αριθµό των bytes που περιέχει ένα αρχείο. Το όνοµα του αρχείου θα δίνεται από το χρήστη. char filename[20]; FILE *fp; register long count = 0; /* long: για µεγάλα αρχεία */ printf( ώστε το όνοµα του αρχείου: ); scanf( %s, filename); /* άνοιξε το αρχείο */ if ((fp = fopen(filename, r )) == NULL) fprintf(stderr, εν υπάρχει τέτοιο αρχείο!\n ); return(1); while (getc(fp)!= EOF) count++; /* αύξησε κατά 1 το µετρητή για κάθε χαρακτήρα */ /* κλείσε το αρχείο */ fclose(fp); printf( Το αρχείο %s περιέχει %ld bytes.\n, filename, count); Άσκηση 31 22

Να γραφτεί πρόγραµµα που να διαβάζει από το πληκτρολόγιο τα στοιχεία των φοιτητών ενός τµήµατος και να τα αποθηκεύει σ έναν πίνακα δοµών. Κάθε δοµή θα αποτελείται από τα εξής µέλη: Ονοµατεπώνυµο Κωδικός αριθµός Έτος (1 4) Μετά την καταχώρηση των δεδοµένων, ο χρήστης πρέπει να έχει τη δυνατότητα να ζητάει την εµφάνιση των στοιχείων των φοιτητών που φοιτούν σ ένα δεδοµένο έτος. #define MAX_NUMSTUDS 1000 /* µέγιστο πλήθος φοιτητών */ typedef struct char name[20]; short id; short year; Student; /* δοµή εγγραφής φοιτητή */ Student students[250]; /* πίνακας δοµών */ int numstuds; /* αριθµός φοιτητών */ short yea r; /* ζητούµενο έτος */ register int i; /* µετρητής */ printf( Πόσοι είναι οι φοιτητές; (< %d), MAX_NUMSTUDS); scanf( %d, &numstuds); if (numstuds <= 0 numstuds > MAX_NUMSTUDS) fprintf(stderr, Μη αποδεκτός αριθµός!\n ); return(1); printf( \nεισάγετε στοιχεία των φοιτητών:\n ); for (i = 0; i < numstuds; i++) printf( Φοιτητής %d\n, i + 1); printf( Ονοµατεπώνυµο: ); scanf( %s, students[i].name); printf( Κωδικός: ); scanf( %hd, &students[i].id); printf( Έτος: ); scanf( %hd, &students[i].year); printf( Ποιό έτος θέλετε να δείτε; ); scanf( %hd, &year); for (i = 0; i < numstuds; i++) 23

if (students[i].year!= year) continue; printf( %3hd %20s\n, students[i].id, students[i].name); Παρατηρήσεις: 1. Στο παραπάνω πρόγραµµα η προσπέλαση µιας εγγραφής φοιτητή γίνεται µε δείκτη θέσης στον πίνακα των φοιτητών. Εναλλακτικά θα µπορούσαµε να ορίσουµε δείκτη σε δοµή Student (Student *p) που αρχικά να δείχνει στο πρώτο στοιχείο (p = students) και να αναφερόµαστε στα µέλη κάθε στοιχείου του πίνακα µε τη βοήθεια του δείκτη (π.χ. p->name). 2. Θα έπρεπε να υπάρχει και έλεγχος της ορθότητας του έτους σπουδών που εισάγεται, δηλαδή αν είναι αριθµός µεταξύ 1 και 4. 3. Όπως έχει το πρόγραµµα δεν είναι και πολύ χρήσιµο. Μια πιο ρεαλιστική του µορφή θα περιελάµβανε εγγραφή σε αρχείο των εισαγόµενων φοιτητών, καθώς και δυνατότητα αναζήτησης των φοιτητών όχι ενός έτους µόνο αλλά και περισσοτέρων, για όσες φορές επιθυµεί ο χρήστης. É Άσκηση 32 Στο πρόγραµµα της προηγούµενης άσκησης, ο χώρος που καταλαµβάνει στη µνήµη ο πίνακας των φοιτητών είναι σταθερός (MAX_NUMSTUDS επί το µέγεθος της δοµής Student), ανεξάρτητα από το πραγµατικό πλήθος των φοιτητών, κάτι που µπορεί να οδηγεί σε σπατάλη χώρου. Επιπλέον, αυτός ο χώρος µπορεί να αποδειχθεί ανεπαρκής αν ο χρήστης επιθυµεί να εισάγει πάνω από ΜΑΧ_NUMSTUDS εγγραφές φοιτητών. είξτε πώς µπορεί ν αντιµετωπιστεί αυτό το πρόβληµα χρησιµοποιώντας δυναµική (αντί της στατικής) κατανοµή µνήµης. υναµική κατανοµή µνήµης, δηλαδή δέσµευση του χώρου που καταλαµβάνει ο πίνακας κατά την εκτέλεση του προγράµµατος κι όχι εξαρχής, µπορεί να επιτευχθεί µε τη βοήθεια των σχετικών συναρτήσεων της C, malloc και calloc. Επειδή τα πρωτότυπα (δηλώσεις) αυτών των συναρτήσεων βρίσκονται στο αρχείο-επικεφαλίδα <stdlib.h>, αυτό θα πρέπει να συµπεριληφθεί (#include <stdlib.h>) στο πρόγραµµα, µαζί µε το <stdio.h>. Παρακάτω δίνονται τα σηµεία του κώδικα που θα αλλάξουν. #include <stdlib.h>... Student *students;... 24

if ((students = (Student *)malloc(numstuds * sizeof(student))) == NULL) fprintf(stderr, Ανεπαρκής µνήµη!\n ); return(2); /* εναλλακτικά, ειδικά αν πρόκειται για πίνακες όπως εδώ, χρησιµοποιείται η calloc: if ((students = (Student *)calloc(numstuds, sizeof(student))) == NULL) fprintf(stderr, Ανεπαρκής µνήµη!\n ); return(2); */... /* απελευθέρωση δεσµευµένης µνήµης */ free(students); Ασκήσεις χωρίς απαντήσεις Άσκηση Α.1 Κάντε την Άσκηση 6 χρησιµοποιώντας τον τελεστή υπολοίπου ακέραιας διαίρεσης, % (Για απλότητα, θεωρείστε ότι ο αριθµός είναι µη-αρνητικός). Χρησιµοποιείστε το γεγονός ότι ένας αριθµός είναι άρτιος αν και µόνο αν το υπόλοιπο της διαίρεσής του µε το 2 είναι µηδέν. Άσκηση Α.2 Κάντε την Άσκηση 16 για το παρακάτω πρόγραµµα. Ποιά εντολή βρόχου θα χρησιµοποιούσατε εδώ; int x = 5; start: printf( %d\n, x); if (x--) goto start; 25

Άσκηση Α.3 Γράψτε συνάρτηση που, δοσµένου ενός πλήθους συχνοτήτων, να υπολογίζει τις αντίστοιχες δεξιόστροφες αθροιστικές συχνότητες. (Υπόδειξη: Τοποθετείστε τις συχνότητες σε πίνακες.) Άσκηση Α.4 Ποιο είναι το λάθος στο ακόλουθο απόσπασµα κώδικα; int i, *ip; ip = &i; ip *= 3; Άσκηση Α.5 Ξαναγράψτε το πρόγραµµα της Άσκησης 22 λαµβάνοντας υπόψη τις Παρατηρήσεις 1, 2. Άσκηση Α.6 Κάντε την Άσκηση Α.5 µε γινόµενο πινάκων αντί αθροίσµατος. Να συµπεριλάβετε έλεγχο συµφωνίας των διαστάσεων των πινάκων (Υπενθύµιση: Στο γινόµενο AB ο Β πρέπει να έχει τόσες γραµµές όσες στήλες έχει ο Α.). Άσκηση Α.7 Ξαναγράψτε τις συναρτήσεις µιγαδικής αφαίρεσης και διαίρεσης εκµεταλλευόµενοι τις συναρτήσεις µιγαδικής πρόσθεσης και πολλαπλασιασµού, αντίστοιχα. Θα χρειαστεί να γράψετε συναρτήσεις που να επιστρέφουν τον αντίθετο και τον αντίστροφο ενός µιγαδικού αριθµού. (Υπενθύµιση: z = x iy και 1/ z = z / z. ) Άσκηση Α.8 Ξαναγράψτε το πρόγραµµα της Άσκησης 30 λαµβάνοντας υπόψη τις Παρατηρήσεις που το συνοδεύουν. Άσκηση Α.9 Τι λάθος/παράλειψη υπάρχει στο παρακάτω απόσπασµα κώδικα; float *a; register int i; int N; printf( Πόσοι αριθµοί; ); scanf( %d, &N); printf( Εισάγετε τους αριθµούς: ); for (i = 0; i < N; i++); scanf( %f, a + i); 2 26