ΟΝΟΜΑΤΕΠΩΝΥΜΟ: AEM: ΜΕΡΟΣ Α: ΕΡΩΤΗΣΕΙΣ ΠΟΛΛΑΠΛΩΝ ΕΠΙΛΟΓΩΝ [15 µονάδες] ΣΗΜΑΝΤΙΚΕΣ ΔΙΕΥΚΡΙΝΙΣΕΙΣ: Επιλέξτε ΜΙΑ σωστή απάντηση για κάθε ερώτηση. Λάθος απαντήσεις βαθµολογούνται αρνητικά Σε ερωτήσεις που περιέχουν κοµµάτια κώδικα µπορείτε να υποθέσετε ότι το υπόλοιπο πρόγραµµα στο οποίο περιλαµβάνονται αυτά τα κοµµάτια είναι καθόλα σωστό. Υποθέστε πως αν υπάρχει πιθανότητα να προκληθεί segmentation fault λόγω λάθους στον κώδικα, τότε θα προκληθεί. Όπου εµφανίζεται κλήση malloc θεωρήστε ότι θα ολοκληρωθεί επιτυχώς (χωρίς να επιστρέψει NULL). Υποθέστε ότι οι µεταβλητές τύπου int καταλαµβάνουν πάντα 4 bytes. 1. Ποια πρόταση περιγράφει καλύτερα τη λειτουργία του παρακάτω κοµµατιού κώδικα? int x=0; int pin[26]={0; /* αρχικοποίηση όλων των στοιχείων του πίνακα σε μηδέν */ while(1) { x = getchar(); if (!isalpha(x)) break; pin[toupper(x)-'a']++; A) Επικίνδυνο: µπορεί να γράψει εκτός των ορίων του πίνακα. B) Δε µεταγλωττίζεται: Λάθος στην προσπέλαση του πίνακα pin. C) Δε µεταγλωττίζεται: Λάθος τύπος µεταβλητής. D) Σωστό: Υπολογίζει πλήθος εµφανίσεων των γραµµάτων που διαβάζονται. E) Σωστό: Υπολογίζει πλήθος εµφανίσεων των µη-γραµµάτων που διαβάζονται. 2. Ποια πρόταση περιγράφει καλύτερα τη λειτουργία του παρακάτω κοµµατιού κώδικα αν υποθέσουµε ότι η είσοδος είναι πάντα έγκυρη (δηλαδή ακέραιος αριθµός)? int x; do { scanf("%d", &x); while (x < 5 x > 3); A) Δε µπορούµε να γνωρίζουµε, γιατί δεν έχει αρχικοποιηθεί το x. B) Ατέρµονος βρόχος C) Βρόχος που επαναλαµβάνεται όσο το x δεν είναι µεταξύ 3 και 5 D) Βρόχος που επαναλαµβάνεται όσο το x είναι µεταξύ 3 και 5 E) Καµία από τις παραπάνω
3. Υποθέτοντας ότι αρχικά η τιµή της µεταβλητής partial είναι µεγαλύτερη από την τιµή της final, τι πρέπει να γίνει στο σώµα του παρακάτω βρόχου ώστε να µην είναι ατέρµονος? while (partial > final) {... A) Τόσο η partial όσο και η final πρέπει να αυξάνονται µε τον ίδιο ρυθµό B) Τόσο η partial όσο και η final πρέπει να µειώνονται µε τον ίδιο ρυθµό C) Πρέπει να αυξάνεται η partial ή να µειώνεται η final D) Πρέπει να µειώνεται η partial ή να αυξάνεται η final E) Δε χρειάζεται να γίνει κάτι 4. Ποια πρόταση περιγράφει καλύτερα τη λειτουργία του παρακάτω κοµµατιού κώδικα? int x, pin[10], start = rand()%10; for (x = start; x < start+10; x++) { scanf("%d", &pin[x%10]); A) Μπορεί να γράψει εκτός των ορίων του πίνακα. B) Ατέρµονος βρόχος C) Ο βρόχος µπορεί να µην εκτελεστεί ποτέ. D) Γεµίζει µερικά κελιά του πίνακα µε αριθµούς που διαβάζουµε από το πληκτρολόγιο. E) Γεµίζει όλα τα κελιά του πίνακα µε αριθµούς που διαβάζουµε από το πληκτρολόγιο. 5. Τι θα περιέχει ο πίνακας pin µετά την εκτέλεση του παρακάτω κώδικα? int i=9, pin[10]={0; /* αρχικοποίηση περιεχοµένων πίνακα σε 0 */ while(i) { pin[i] = i--; A) Δε µπορούµε να γνωρίζουµε γιατί ο βρόχος είναι ατέρµονος. B) -1 0 1 2 3 4 5 6 7 8 C) 0 1 2 3 4 5 6 7 8 9 D) 9 8 7 6 5 4 3 2 1 0 E) 8 7 6 5 4 3 2 1 0-1 6. Τι θα εκτυπωθεί στην οθόνη µετά την εκτέλεση του παρακάτω κώδικα? int pin[] = {7, 9, 11, 13, 15, 17; int *p1 = pin; int *p2 = &pin[5]; printf("%d", p2-p1); A) Κάποια διεύθυνση (π.χ. 0xfe12a45b) B) 5 C) 8 D) 10 E) 20
7. Τι θα εκτυπωθεί στην οθόνη µετά την εκτέλεση του παρακάτω κώδικα? int pin[] = {7, 9, 11, 13, 15, 17; int *p1 = pin; int *p2 = &pin[5]; printf("%d", (char*)p2-(char*)p1); A) Κάποια διεύθυνση (π.χ. 0xfe12a45b) B) 5 C) 8 D) 10 E) 20 8. Ποια εντολή πρέπει να µπει στο κενό κουτί ώστε τα περιεχόµενα του πίνακα να γίνουν {1, 2, 3, 10, 5? int p[] = {1, 2, 3, 4, 5; int *ptr = p; A) *ptr + 3 = 10; B) *ptr[3] = 10; C) *(ptr+3) = 10; D) (*ptr)[3] = 10; E) *(ptr[3]) = 10; 9. Μελετήστε την παρακάτω συνάρτηση και απαντήστε τι θα υπολογίσει αν κληθεί για κάποια συµβολοσειρά str, κάποιο χαρακτήρα c και i=0 int mystery(char *str, char c, int i) { if (i == strlen(str)) return 0; if (c == *(str+i)) return mystery(str, c, i+1)+1; return mystery(str, c, i+1); A) Το µήκος της συµβολοσειράς str B) Το µήκος της συµβολοσειράς str από την πρώτη εµφάνιση του χαρακτήρα c σε αυτή και µετά. C) Το πλήθος εµφανίσεων του χαρακτήρα c στη συµβολοσειρά str. D) 1 αν ο χαρακτήρας c εµφανίζεται στη συµβολοσειρά str, διαφορετικά 0. E) 1 αν η συµβολοσειρά αποτελείται αποκλειστικά από χαρακτήρες ίσους µε τον c, διαφορετικά 0. 10. Τι θα εκτυπώσει το παρακάτω κοµµάτι κώδικα? char str[20], *ptr; strcpy(str, "abcd"); ptr = str+4; printf("%c", *ptr); A) κενό χαρακτήρα ('\0', το οποίο είναι µη εκτυπώσιµο) B) Το χαρακτήρα d C) Τίποτα γιατί η printf είναι συντακτικά λάθος. D) Τίποτα, γιατί η ανάθεση είναι συντακτικά λάθος E) Τίποτα γιατί θα προκληθεί segmentation fault
11. Τι θα εκτυπώσει το παρακάτω κοµµάτι κώδικα? char str[20]; strcpy(str, "Basketball"); *strchr(str, 'l') = 'r'; printf("%s", str); A) Basketbarr B) Basketbarl C) Τίποτα γιατί θα προκληθεί segmentation fault. D) Τίποτα γιατί ο κώδικας δεν µεταγλωττίζεται επιτυχώς. E) Δε µπορούµε να γνωρίζουµε 12. Η παρακάτω συνάρτηση υποτίθεται πως ελέγχει αν οι συµβολοσειρές str1 και str2 είναι όµοιες. Τι λάθος περιέχει (αν περιέχει λάθος)? int same (char *str1, char *str2) { int i, len=strlen(str1); if (strlen(str1)!=strlen(str2)) return 0; for (i=0; i<len; i++ ) { if (*(str1++)!= *(str2++)) return 0; return 1; A) Αντί για *(str1++) έπρεπε να έχει (*str1)++. Οµοίως για το str2 B) Μεταβάλει τις τιµές των str1, str2 κι όταν επιστρέψουµε από τη συνάρτηση δε θα έχουµε πρόσβαση στις συµβολοσειρές, εφόσον αυτές έχουν περαστεί µε αναφορά (by reference) C) Δε θα ελέγξει ολόκληρες τις συµβολοσειρές D) Μετά την εντολή if έπρεπε να έχει else return 1; E) Δεν υπάρχει λάθος, η συνάρτηση λειτουργεί σωστά. 13. Τι εκτυπώνει το παρακάτω κοµµάτι κώδικα αν η παράµετρος έχει τιµή blue? enum colors {red, white, blue; void print(enum colors c) { switch(c) { case "red": printf("my name is red"); break; case "white": printf("my name is white"); break; default: printf("my name must be blue"); break; A) my name must be blue B) Τίποτα γιατί δεν ταιριάζει καµία περίπτωση C) Τίποτα γιατί δεν επιτρέπεται να χρησιµοποιηθεί τύπος enum ως έκφραση επιλογής σε switch D) Τίποτα γιατί υπάρχει συντακτικό λάθος στις περιπτώσεις της switch. E) Τίποτα γιατί υπάρχει συντακτικό λάθος στον ορισµό της enum.
14. Με δεδοµένο τον παρακάτω ορισµό ενός κόµβου, γράψαµε τη συνάρτηση last2first η οποία θέλουµε να παίρνει ως παράµετρο τη διεύθυνση της κεφαλής µιας απλά διασυνδεδεµένης, µη-κυκλικής λίστας χωρίς τερµατικό, να µεταφέρει τον τελευταίο κόµβο στην αρχή, και να επιστρέφει τη νέα κεφαλή της λίστας. Ποιες εντολές λείπουν στις τρεις κενές γραµµές του κώδικα? typedef struct node { int val; struct node *nxt; nodet; nodet* last2first(nodet *head) { nodet *p = NULL, *q = head; if (head == NULL head->nxt == NULL) return head; while(q->nxt) { p = q; q = q->nxt; return head; A) p = NULL; q->nxt = head; B) p->nxt = NULL; q->nxt = head; C) q->nxt = p; p->nxt = NULL; D) p->nxt = NULL; q->nxt = head; E) q->nxt = p; p = NULL; 15. Δεδοµένης της παρακάτω δοµής (struct), κατασκευάζουµε µια διπλά διασυνδεδεµένη, µη-κυκλική λίστα, χωρίς τερµατικό (sentinel). Η λίστα περιέχει µε τη σειρά τα στοιχεία 0-2-4-6-8, η µεταβλητή head δείχνει στον κόµβο µε περιεχόµενα 0 (κεφαλή της λίστας) και η µεταβλητή current δείχνει στον κόµβο µε περιεχόµενα 4. Τι θα συµβεί όταν εκτελεστεί η εντολή που βρίσκεται στο κουτί? struct node { int val; struct node *next; struct node *prev; *head, *current; current->next->next = current; A) Ο επόµενος κόµβος του 6 θα γίνει αυτός που περιέχει το 4 B) Ο επόµενος κόµβος του 8 θα γίνει αυτός που περιέχει το 4 C) Τα περιεχόµενα του κόµβου που περιέχει το 6 θα γίνουν 4 D) Τα περιεχόµενα του κόµβου που περιέχει το 8 θα γίνουν 4 E) Ο δείκτης current θα δείχνει στον κόµβο που περιέχει το 8
ΜΕΡΟΣ B: ΣΥΝΤΟΜΕΣ ΑΠΑΝΤΗΣΕΙΣ [15 µονάδες] Δώστε συνοπτικές απαντήσεις στις ερωτήσεις που ακολουθούν. 1. Μελετήστε τους παρακάτω ορισµούς/δηλώσεις: typedef struct node { char * name; struct node *next; listt; /* δείκτης σε συµβολοσειρά (όνοµα)*/ /* δείκτης προς επόµενο κόµβο λίστας*/ listt *head; /* δείκτης προς την κεφαλή λίστας */ Δεδοµένων των παραπάνω ορισµών, έχουµε ήδη κατασκευάσει µια µη-κυκλική, απλά διασυνδεδεµένη λίστα, έτσι ώστε τα ονόµατα που περιέχονται σε αυτή να βρίσκονται αποθηκευµένα στους κόµβους σε αύξουσα αλφαβητική σειρά. Ο δείκτης head δείχνει στον πρώτο κόµβο (κεφαλή) αυτής της λίστας και είναι καθολική µεταβλητή. Συµπληρώστε τον κώδικα της παρακάτω συνάρτησης η οποία, δεδοµένου ενός νέου ονόµατος αποθηκευµένου στην παράµετρο µε όνοµα newname, βρίσκει κι επιστρέφει τον κόµβο της λίστας µετά από τον οποίο θα πρέπει να εισαχθεί το νέο όνοµα, ή NULL αν η εισαγωγή πρέπει να γίνει στην αρχή της λίστας. Για παράδειγµα, αν η λίστα είναι µη-κενή και περιέχει τα ονόµατα ("bob", "david", "tom") και καλέσουµε τη συνάρτηση µε παράµετρο το όνοµα "ernie", τότε η συνάρτηση πρέπει να επιστρέψει δείκτη προς τον κόµβο "david". Αν όµως καλέσουµε τη συνάρτηση µε παράµετρο το όνοµα "alex", τότε πρέπει να επιστρέψει NULL. Η συνάρτησή σας ΔΕΝ πρέπει να εισάγει νέο κόµβο στη λίστα. listt * findprev (char *newname) {
2. Ποια ή ποιες από τις παρακάτω συναρτήσεις δεν κάνουν σωστή χρήση δεικτών? Αιτιολογήστε σύντοµα (εντός του κάθε κουτιού). int * f1 () { int x = 1; return &x; int * f2 () { int *px; *px = 2; return px; int * f3 () { int *px = (int*) malloc(sizeof(int)); *px = 3; return px; int * f4 () { int *px = (int*) malloc(sizeof(int)); *px = 4; free(px); return px; 3. Μελετήστε τους παρακάτω ορισµούς/δηλώσεις: typedef struct { char *firstname; /* µικρό όνοµα */ char *lastname; /* επώνυµο */ namet; /* στοιχεία ενός ονόµατος */ typedef struct { namet name; double salary; employeet; /* στοιχεία ενός υπαλλήλου */ (α) Δηλώστε ένα πίνακα στον οποίο µπορούν να αποθηκευτούν τα στοιχεία 100 υπαλλήλων. (β) Για τον πρώτο υπάλληλο στον πίνακα, θέστε το µισθό του σε 123000, το µικρό του όνοµα σε John και το επώνυµό του σε Smith.
ΠΡΟΧΕΙΡΟ