📚 Ασκήσεις με Λύσεις

Εισαγωγή στον Προγραμματισμό - Γλώσσα C

📝 Άσκηση 1: Αριθμοί Armstrong

📋 Εκφώνηση

📌 Ζητούμενο

Ένας αριθμός λέγεται αριθμός Armstrong αν το άθροισμα των κύβων των ψηφίων του είναι ίσο με τον ίδιο τον αριθμό.

Να γραφεί πρόγραμμα σε C που θα ζητά άγνωστο πλήθος αριθμών και θα εμφανίζει αν ο αριθμός που δόθηκε είναι αριθμός Armstrong ή όχι.

Η εισαγωγή αριθμών θα σταματά όταν δοθεί ο αριθμός μηδέν (0).

Για τον έλεγχο των αριθμών δύναται να χρησιμοποιηθεί συνάρτηση που θα κατασκευάσετε για το σκοπό αυτό.

Παράδειγμα: 153 = 1³ + 5³ + 3³ = 1 + 125 + 27 = 153

🔍 Ανάλυση

💡 Τι είναι ο Αριθμός Armstrong;

Ένας τριψήφιος αριθμός είναι Armstrong όταν:

αριθμός = (1ο ψηφίο)³ + (2ο ψηφίο)³ + (3ο ψηφίο)³

📊 Παραδείγματα Αριθμών Armstrong

Αριθμός Υπολογισμός Αποτέλεσμα Armstrong;
1531³ + 5³ + 3³ = 1 + 125 + 27153✅ Ναι
3703³ + 7³ + 0³ = 27 + 343 + 0370✅ Ναι
3713³ + 7³ + 1³ = 27 + 343 + 1371✅ Ναι
4074³ + 0³ + 7³ = 64 + 0 + 343407✅ Ναι
1231³ + 2³ + 3³ = 1 + 8 + 2736❌ Όχι

🧩 Αλγόριθμος

Βήμα 1: Εξαγωγή Ψηφίων

  • αριθμός % 10 → δίνει το τελευταίο ψηφίο
  • αριθμός / 10αφαιρεί το τελευταίο ψηφίο

Βήμα 2: Υπολογισμός Κύβου

Ο κύβος ενός ψηφίου: ψηφίο * ψηφίο * ψηφίο

Βήμα 3: Σύγκριση

Αν το άθροισμα των κύβων = αρχικός αριθμός → είναι Armstrong!

✅ Λύση

/* Πρόγραμμα: Έλεγχος Αριθμών Armstrong */ #include <stdio.h> // Συνάρτηση που ελέγχει αν ένας αριθμός είναι Armstrong int isArmstrong(int num) { int original = num; // Κρατάμε τον αρχικό αριθμό int digit, sum = 0; // digit: κάθε ψηφίο, sum: άθροισμα κύβων while (num > 0) { digit = num % 10; // Παίρνουμε το τελευταίο ψηφίο sum += digit * digit * digit; // Προσθέτουμε τον κύβο του num /= 10; // Αφαιρούμε το τελευταίο ψηφίο } if (sum == original) return 1; // Είναι Armstrong else return 0; // Δεν είναι Armstrong } int main() { int number; do { printf("Δώσε έναν αριθμό (0 για τερματισμό): "); scanf("%d", &number); if (number != 0) { if (isArmstrong(number)) printf("Ο αριθμός %d είναι αριθμός Armstrong.\n", number); else printf("Ο αριθμός %d ΔΕΝ είναι αριθμός Armstrong.\n", number); } } while (number != 0); printf("Τέλος προγράμματος.\n"); return 0; }

🔎 Ανάλυση Κώδικα

📌 Η Συνάρτηση isArmstrong()

  • Είσοδος: Ένας ακέραιος αριθμός num
  • Έξοδος: Επιστρέφει 1 αν είναι Armstrong, 0 αν δεν είναι

⚠️ Σημαντικό

Αποθηκεύουμε τον αρχικό αριθμό στη μεταβλητή original πριν ξεκινήσουμε την επεξεργασία, γιατί η μεταβλητή num θα γίνει 0 μετά το while loop!

📝 Παράδειγμα Εκτέλεσης Βήμα-Βήμα (για 153)

Επανάληψηnumdigitdigit³sum
Αρχικά153--0
153 → 1532727
15 → 15125152
1 → 011153

🖥️ Παράδειγμα Εξόδου

Δώσε έναν αριθμό (0 για τερματισμό): 153 Ο αριθμός 153 είναι αριθμός Armstrong. Δώσε έναν αριθμό (0 για τερματισμό): 123 Ο αριθμός 123 ΔΕΝ είναι αριθμός Armstrong. Δώσε έναν αριθμό (0 για τερματισμό): 0 Τέλος προγράμματος.

✅ Έννοιες που χρησιμοποιήθηκαν:

Συναρτήσεις, do-while loop, while loop, τελεστής %, τελεστής /, εμφωλευμένα if


🎭 Άσκηση 2: Σύστημα Κρατήσεων Θεάτρου

📋 Εκφώνηση

📌 Ζητούμενο

Ένα θέατρο διαθέτει αίθουσα χωρητικότητας 500 θέσεων.

Όταν κάποιος ενδιαφέρεται να «κλείσει» θέσεις για την παράσταση, τηλεφωνεί και δηλώνει τον αριθμό των εισιτηρίων που επιθυμεί:

  • Αν οι κενές θέσεις επαρκούν τότε γίνεται η κράτηση
  • Αν οι απαιτούμενες θέσεις είναι περισσότερες από τις κενές τότε δεν γίνεται η κράτηση

Η διαδικασία επαναλαμβάνεται μέχρι να γεμίσει η θεατρική αίθουσα (freeSeats = 0).

🔍 Ανάλυση

✅ Σενάριο 1: Επιτυχής κράτηση

Κενές: 500, Ζητούμενα: 50 → 50 ≤ 500 → Κράτηση OK! → Νέες κενές: 450

❌ Σενάριο 2: Αποτυχημένη κράτηση

Κενές: 30, Ζητούμενα: 50 → 50 > 30 → Αποτυχία! → Κενές παραμένουν: 30

📊 Ροή Προγράμματος

🎬 Αρχή: freeSeats = 500
🔄 Όσο freeSeats > 0
📥 Διάβασε requested
✅ Ναι: freeSeats -= requested | ❌ Όχι: Αποτυχία
🏁 Τέλος: "Η αίθουσα γέμισε!"

✅ Λύση

/* Πρόγραμμα: Σύστημα Κρατήσεων Θεάτρου */ #include <stdio.h> int main(void) { const int CAPACITY = 500; // Σταθερά χωρητικότητας int freeSeats = CAPACITY; // Αρχικές κενές θέσεις int requested; // Ζητούμενα εισιτήρια printf("Θεατρική αίθουσα χωρητικότητας %d θέσεων.\n", CAPACITY); while (freeSeats > 0) { printf("\nΚενές θέσεις: %d\n", freeSeats); printf("Δώσε αριθμό εισιτηρίων για κράτηση: "); // Έλεγχος έγκυρης εισόδου if (scanf("%d", &requested) != 1) { printf("Μη έγκυρη είσοδος. Τερματισμός.\n"); return 1; } // Έλεγχος για θετικό αριθμό if (requested <= 0) { printf("Ο αριθμός εισιτηρίων πρέπει να είναι θετικός.\n"); continue; } // Έλεγχος αν επαρκούν οι θέσεις if (requested <= freeSeats) { freeSeats -= requested; printf("Η κράτηση ολοκληρώθηκε! Κρατήθηκαν %d εισιτήρια.\n", requested); } else { printf("Αποτυχία κράτησης: ζητήθηκαν %d, αλλά υπάρχουν μόνο %d κενές θέσεις.\n", requested, freeSeats); } } printf("\nΗ αίθουσα γέμισε! Δεν υπάρχει καμία κενή θέση.\n"); return 0; }

🔎 Ανάλυση Κώδικα

📌 Μεταβλητές

ΜεταβλητήΤύποςΠεριγραφή
CAPACITYconst intΣταθερά χωρητικότητας (500)
freeSeatsintΤρέχουσες κενές θέσεις
requestedintΖητούμενα εισιτήρια

⚠️ Σημαντικά

  • const: Δηλώνει σταθερά που δεν αλλάζει
  • continue: Επιστρέφει στην αρχή του βρόχου
  • -=: Σύνθετος τελεστής (freeSeats = freeSeats - requested)

🖥️ Παράδειγμα Εξόδου

Θεατρική αίθουσα χωρητικότητας 500 θέσεων. Κενές θέσεις: 500 Δώσε αριθμό εισιτηρίων για κράτηση: 150 Η κράτηση ολοκληρώθηκε! Κρατήθηκαν 150 εισιτήρια. Κενές θέσεις: 350 Δώσε αριθμό εισιτηρίων για κράτηση: 400 Αποτυχία κράτησης: ζητήθηκαν 400, αλλά υπάρχουν μόνο 350 κενές θέσεις. Κενές θέσεις: 350 Δώσε αριθμό εισιτηρίων για κράτηση: 350 Η κράτηση ολοκληρώθηκε! Κρατήθηκαν 350 εισιτήρια. Η αίθουσα γέμισε! Δεν υπάρχει καμία κενή θέση.

✅ Έννοιες που χρησιμοποιήθηκαν:

const, while loop, scanf validation, continue, τελεστής -=, return codes


⚖️ Άσκηση 3: Νομική Εταιρία Prins & Associates

📋 Εκφώνηση

📌 Ζητούμενο

Σε μια νομική εταιρία Prins & Associates εργάζονται 15 νομικοί.

Να γραφεί πρόγραμμα σε C το οποίο:

  1. Θα διαβάζει τα ονόματα στον πίνακα NAMES[15][40] και τον αριθμό υποθέσεων στον πίνακα CASES[15]
  2. Για κάθε νομικό θα διαβάζει το ποσοστό επιτυχίας [0,1], θα το πολλαπλασιάζει με τις υποθέσεις και θα αποθηκεύει στον RESULTS[15]
  3. Θα ταξινομεί φθίνουσα τον RESULTS και παράλληλα τους NAMES, CASES με συνάρτηση
  4. Θα εμφανίζει το όνομα του καλύτερου νομικού

🔍 Ανάλυση

💡 Δομή Δεδομένων

ΠίνακαςΤύποςΜέγεθοςΠεριεχόμενο
NAMESchar[15][40]Ονόματα νομικών
CASESint[15]Αριθμός υποθέσεων
RESULTSfloat[15]Βαθμολογία = υποθέσεις × ποσοστό

Παράδειγμα Υπολογισμού:

Νομικός: Παπαδόπουλος | Υποθέσεις: 50 | Ποσοστό: 0.85

RESULTS = 50 × 0.85 = 42.5

✅ Λύση

/* Πρόγραμμα: Νομική Εταιρία Prins & Associates */ #include <stdio.h> #include <string.h> #define N 15 #define MAX_NAME 40 // Συνάρτηση ταξινόμησης (Bubble Sort - Φθίνουσα) void sortDescending(char NAMES[][MAX_NAME], int CASES[], float RESULTS[], int size) { int i, j; float tempResult; int tempCase; char tempName[MAX_NAME]; for (i = 0; i < size - 1; i++) { for (j = 0; j < size - 1 - i; j++) { if (RESULTS[j] < RESULTS[j + 1]) { // Αντιμετάθεση RESULTS tempResult = RESULTS[j]; RESULTS[j] = RESULTS[j + 1]; RESULTS[j + 1] = tempResult; // Αντιμετάθεση CASES tempCase = CASES[j]; CASES[j] = CASES[j + 1]; CASES[j + 1] = tempCase; // Αντιμετάθεση NAMES strcpy(tempName, NAMES[j]); strcpy(NAMES[j], NAMES[j + 1]); strcpy(NAMES[j + 1], tempName); } } } } int main() { char NAMES[N][MAX_NAME]; int CASES[N]; float RESULTS[N]; float successRate; int i; printf("=== Νομική Εταιρία Prins & Associates ===\n\n"); // ΒΗΜΑ i: Εισαγωγή ονομάτων και υποθέσεων for (i = 0; i < N; i++) { printf("Νομικός %d:\n", i + 1); printf(" Όνομα: "); scanf("%s", NAMES[i]); printf(" Αριθμός υποθέσεων: "); scanf("%d", &CASES[i]); } // ΒΗΜΑ ii: Υπολογισμός RESULTS printf("\n--- Εισαγωγή Ποσοστών Επιτυχίας ---\n"); for (i = 0; i < N; i++) { printf("Ποσοστό επιτυχίας για %s [0-1]: ", NAMES[i]); scanf("%f", &successRate); RESULTS[i] = CASES[i] * successRate; } // ΒΗΜΑ iii: Ταξινόμηση φθίνουσα sortDescending(NAMES, CASES, RESULTS, N); // Εμφάνιση αποτελεσμάτων printf("\n=== Κατάταξη Νομικών (Φθίνουσα) ===\n"); printf("%-5s %-20s %-12s %-10s\n", "#", "Όνομα", "Υποθέσεις", "Βαθμολογία"); printf("--------------------------------------------\n"); for (i = 0; i < N; i++) { printf("%-5d %-20s %-12d %-10.2f\n", i + 1, NAMES[i], CASES[i], RESULTS[i]); } // ΒΗΜΑ iv: Καλύτερος νομικός printf("\n🏆 Ο καλύτερος νομικός είναι: %s\n", NAMES[0]); printf(" Βαθμολογία: %.2f\n", RESULTS[0]); return 0; }

🔎 Ανάλυση Κώδικα

📌 Η Συνάρτηση Ταξινόμησης

  • Χρησιμοποιεί Bubble Sort
  • Κάθε swap στον RESULTS γίνεται παράλληλα και στους NAMES, CASES
  • Διατηρείται η αντιστοιχία μεταξύ των πινάκων

⚠️ Σημαντικά

  • strcpy(): Απαραίτητη για αντιγραφή strings (όχι απλή ανάθεση =)
  • Φθίνουσα: Συνθήκη RESULTS[j] < RESULTS[j+1]
  • 2D πίνακας: Το NAMES[15][40] είναι 15 strings των 40 χαρακτήρων

🖥️ Παράδειγμα Εξόδου

=== Κατάταξη Νομικών (Φθίνουσα) === # Όνομα Υποθέσεις Βαθμολογία -------------------------------------------- 1 Γεωργίου 60 45.00 2 Παπαδόπουλος 50 42.50 3 Αντωνίου 40 36.00 🏆 Ο καλύτερος νομικός είναι: Γεωργίου Βαθμολογία: 45.00

✅ Έννοιες που χρησιμοποιήθηκαν:

2D πίνακες χαρακτήρων, παράλληλοι πίνακες, Bubble Sort, strcpy(), συναρτήσεις με πίνακες, #define, format specifiers


🏋️ Άσκηση 4: Γυμναστήριο - Αξιολόγηση Προπονητών (Pointers)

📋 Εκφώνηση

📌 Ζητούμενο

Σε ένα γυμναστήριο υπάρχουν 12 προπονητές. Για κάθε προπονητή καταχωρούνται:

  • Ονοματεπώνυμο (έως 39 χαρακτήρες) σε NAMES[12][40]
  • Αριθμός πελατών σε CLIENTS[12]
  • Ποσοστό ικανοποίησης (real στο [0,1]) σε SAT[12]

Να γραφεί πρόγραμμα σε C που:

  1. Διαβάζει τα παραπάνω στοιχεία για τους 12 προπονητές.
  2. Υπολογίζει για κάθε προπονητή: SCORE = CLIENTS × SAT και τον αποθηκεύει σε SCORES[12].
  3. Ταξινομεί κατά φθίνουσα σειρά με συνάρτηση που χρησιμοποιεί pointers.
  4. Εμφανίζει το όνομα του καλύτερου προπονητή.
SCORE = CLIENTS × SAT

📚 Θεωρία: Pointers στη C

⭐ Τι είναι ο Pointer;

Ένας pointer είναι μια μεταβλητή που αποθηκεύει τη διεύθυνση μνήμης μιας άλλης μεταβλητής.

Παράδειγμα στη Μνήμη:

0x1000 x = 42
0x2000 p = 0x1000

int x = 42;    int *p = &x;

📌 Σημαντικοί Τελεστές

ΤελεστήςΌνομαΠεριγραφήΠαράδειγμα
&Address-ofΕπιστρέφει τη διεύθυνση&x → διεύθυνση του x
*DereferenceΕπιστρέφει την τιμή στη διεύθυνση*p → τιμή που δείχνει ο p

💡 Pointer Arithmetic

Όταν έχουμε έναν πίνακα float scores[12]:

  • scores = διεύθυνση του πρώτου στοιχείου
  • scores + j = διεύθυνση του scores[j]
  • *(scores + j) = τιμή του scores[j]

Ισοδύναμα: scores[j]*(scores + j)

✅ Λύση

/* Πρόγραμμα: Γυμναστήριο - Αξιολόγηση Προπονητών με Pointers */ #include <stdio.h> #include <string.h> #define N 12 #define NAME_LEN 40 /* swap για float με pointers */ void swapFloat(float *a, float *b) { float tmp = *a; *a = *b; *b = tmp; } /* swap για int με pointers */ void swapInt(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } /* swap για strings (πίνακες char) */ void swapName(char a[NAME_LEN], char b[NAME_LEN]) { char tmp[NAME_LEN]; strcpy(tmp, a); strcpy(a, b); strcpy(b, tmp); } /* Ταξινόμηση (φθίνουσα) με ταυτόχρονη αντιμετάθεση όλων των πινάκων */ void sortDescending(float *scores, int *clients, float *sat, char names[][NAME_LEN], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { /* pointer arithmetic: *(scores + j) αντί scores[j] */ if (*(scores + j) < *(scores + j + 1)) { swapFloat(scores + j, scores + j + 1); swapInt(clients + j, clients + j + 1); swapFloat(sat + j, sat + j + 1); swapName(names[j], names[j + 1]); } } } } int main(void) { char NAMES[N][NAME_LEN]; int CLIENTS[N]; float SAT[N]; float SCORES[N]; for (int i = 0; i < N; i++) { printf("\nΠροπονητής #%d\n", i + 1); printf("Όνομα: "); scanf(" %39[^\n]", NAMES[i]); /* δέχεται και κενά */ printf("Αριθμός πελατών: "); scanf("%d", &CLIENTS[i]); while (CLIENTS[i] < 0) { printf("Λάθος! Ξαναδώσε αριθμό πελατών: "); scanf("%d", &CLIENTS[i]); } printf("Ποσοστό ικανοποίησης (0-1): "); scanf("%f", &SAT[i]); while (SAT[i] < 0.0f || SAT[i] > 1.0f) { printf("Λάθος! Δώσε SAT στο [0,1]: "); scanf("%f", &SAT[i]); } SCORES[i] = CLIENTS[i] * SAT[i]; } sortDescending(SCORES, CLIENTS, SAT, NAMES, N); printf("\n=============================\n"); printf("🏆 Καλύτερος προπονητής: %s\n", NAMES[0]); printf("Πελάτες: %d | SAT: %.2f | SCORE: %.2f\n", CLIENTS[0], SAT[0], SCORES[0]); printf("=============================\n"); return 0; }

🔎 Ανάλυση Κώδικα

⭐ Πώς λειτουργεί το swapFloat με Pointers;

ΓραμμήΕπεξήγηση
float tmp = *a;tmp = τιμή στη διεύθυνση a
*a = *b;τιμή στη διεύθυνση a = τιμή στη διεύθυνση b
*b = tmp;τιμή στη διεύθυνση b = tmp

Κλήση: swapFloat(scores + j, scores + j + 1);

Περνάμε τις διευθύνσεις των στοιχείων, όχι τις τιμές τους!

📌 Σύγκριση: Με και Χωρίς Pointers

Χωρίς PointersΜε Pointers
scores[j]*(scores + j)
&scores[j]scores + j
scores[j+1]*(scores + j + 1)

⚠️ Σημαντικά

  • scanf με %39[^\n]: Διαβάζει μέχρι 39 χαρακτήρες ή newline (επιτρέπει κενά στο όνομα)
  • Έλεγχος εγκυρότητας: While loops για επαναδιάβασμα σε περίπτωση λάθους
  • 0.0f: Το 'f' δηλώνει ότι είναι float (όχι double)

🖥️ Παράδειγμα Εξόδου

Προπονητής #1 Όνομα: Γιάννης Παπαδόπουλος Αριθμός πελατών: 50 Ποσοστό ικανοποίησης (0-1): 0.92 Προπονητής #2 Όνομα: Μαρία Κωνσταντίνου Αριθμός πελατών: 45 Ποσοστό ικανοποίησης (0-1): 0.88 ... ============================= 🏆 Καλύτερος προπονητής: Νίκος Αντωνίου Πελάτες: 60 | SAT: 0.85 | SCORE: 51.00 =============================

✅ Έννοιες που χρησιμοποιήθηκαν:

Pointers, Pointer arithmetic (*(scores + j)), Swap με pointers, Τελεστής & (address-of), Τελεστής * (dereference), scanf με [^\n], Έλεγχος εγκυρότητας εισόδου, Παράλληλοι πίνακες