Προγραμματισμός C
Κεφάλαιο 5: Πίνακες και Συναρτήσεις

5.1 Πίνακες Μιας Διάστασης

🎯 Τι είναι Πίνακας;

Ένας πίνακας (array) είναι μια συλλογή από πολλές μεταβλητές του ίδιου τύπου, αποθηκευμένες συνεχόμενα στη μνήμη.

Αντί να δηλώσεις 100 ξεχωριστές μεταβλητές, δηλώνεις έναν πίνακα με 100 θέσεις!

Δήλωση Πίνακα

τύπος όνομα[μέγεθος];

📋 Βασικά Στοιχεία Πινάκων

  • Οι θέσεις αριθμούνται από 0 μέχρι μέγεθος-1
  • Όλα τα στοιχεία έχουν τον ίδιο τύπο
  • Το μέγεθος πρέπει να είναι σταθερό

📘 Παράδειγμα 1 - Δήλωση και Αρχικοποίηση

#include <stdio.h> int main() { // Δήλωση πίνακα με 5 ακέραιους int numbers[5]; // Αρχικοποίηση κάθε θέσης numbers[0] = 10; numbers[1] = 20; numbers[2] = 30; numbers[3] = 40; numbers[4] = 50; // Εμφάνιση των στοιχείων printf("Τα στοιχεία του πίνακα:\n"); for (int i = 0; i < 5; i++) { printf("numbers[%d] = %d\n", i, numbers[i]); } return 0; }

📘 Παράδειγμα 2 - Αρχικοποίηση κατά τη Δήλωση

#include <stdio.h> int main() { // Αρχικοποίηση κατά τη δήλωση int scores[5] = {85, 92, 78, 95, 88}; // Υπολογισμός μέσου όρου int sum = 0; for (int i = 0; i < 5; i++) { sum += scores[i]; } float average = sum / 5.0; printf("Μέσος όρος βαθμών: %.2f\n", average); return 0; }

5.2 Δισδιάστατοι Πίνακες

🎯 Δισδιάστατοι Πίνακες

Οι δισδιάστατοι πίνακες αποτελούνται από γραμμές και στήλες (όπως ένας πίνακας ή μια μήτρα).

τύπος όνομα[γραμμές][στήλες];

📋 Παράδειγμα Δήλωσης

int matrix[3][4]; // 3 γραμμές x 4 στήλες = 12 στοιχεία

Πλήθος στοιχείων: γραμμές × στήλες

Στήλη 0 Στήλη 1 Στήλη 2
Γραμμή 0 a[0][0] a[0][1] a[0][2]
Γραμμή 1 a[1][0] a[1][1] a[1][2]
Γραμμή 2 a[2][0] a[2][1] a[2][2]

📘 Παράδειγμα 3 - Δισδιάστατος Πίνακας

#include <stdio.h> int main() { // Δήλωση και αρχικοποίηση δισδιάστατου πίνακα 3x3 int matrix[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; printf("Η μήτρα 3x3:\n"); // Εμφάνιση του πίνακα for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { printf("%3d ", matrix[i][j]); } printf("\n"); } return 0; }

5.3 Συναρτήσεις

🎯 Τι είναι οι Συναρτήσεις;

Μία συνάρτηση είναι ένα ανεξάρτητο τμήμα κώδικα που εκτελεί μία συγκεκριμένη εργασία και προαιρετικά επιστρέφει μία τιμή.

✅ Πλεονεκτήματα Συναρτήσεων

  • Διαίρεση του προγράμματος σε μικρότερα, διαχειρίσιμα τμήματα
  • Επαναχρησιμοποίηση κώδικα
  • Ευκολότερη συντήρηση και έλεγχος
  • Καλύτερη οργάνωση και δομή του κώδικα

Δήλωση Συνάρτησης

τύπος_επιστροφής όνομα_συνάρτησης(λίστα_παραμέτρων);

📘 Παραδείγματα Δηλώσεων

int add(int a, int b); float calculateAverage(float x, float y); void printMessage(void);

Ορισμός Συνάρτησης

τύπος_επιστροφής όνομα_συνάρτησης(λίστα_παραμέτρων) { /* σώμα συνάρτησης */ return τιμή; }

📘 Παράδειγμα 4 - Απλή Συνάρτηση

#include <stdio.h> // Πρωτότυπο συνάρτησης void printWelcome(void); int main() { printf("Αρχή προγράμματος\n"); // Κλήση συνάρτησης printWelcome(); printf("Τέλος προγράμματος\n"); return 0; } // Ορισμός συνάρτησης void printWelcome(void) { printf("=============================\n"); printf(" Καλώς ήρθατε στη C!\n"); printf("=============================\n"); }

📘 Παράδειγμα 5 - Συνάρτηση με Παραμέτρους

#include <stdio.h> // Πρωτότυπα συναρτήσεων int add(int a, int b); int multiply(int a, int b); int main() { int x = 10, y = 5; int sum, product; // Κλήση συναρτήσεων sum = add(x, y); product = multiply(x, y); printf("x = %d, y = %d\n", x, y); printf("Άθροισμα: %d\n", sum); printf("Γινόμενο: %d\n", product); return 0; } // Ορισμοί συναρτήσεων int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; }

Κατανόηση Πρωτοτύπων Συναρτήσεων

🔍 Ο Ρόλος των "Πρωτοτύπων Συναρτήσεων"

Αυτός είναι ο λόγος ύπαρξης των γραμμών:

int add(int a, int b); int multiply(int a, int b);

Με αυτές τις δηλώσεις λες στον μεταγλωττιστή:

"Υπάρχουν δύο συναρτήσεις με αυτά τα ονόματα και αυτές τις παραμέτρους — θα τις ορίσω κάπου πιο κάτω."

Έτσι, όταν στη main() βλέπει:

sum = add(x, y);

ο μεταγλωττιστής ξέρει:

  • ότι υπάρχει η add,
  • ότι παίρνει δύο int,
  • ότι επιστρέφει int.

Δεν ξέρει ακόμα τι κάνει μέσα, αλλά αυτό δεν χρειάζεται εκείνη τη στιγμή· του αρκεί να γνωρίζει πώς να τη χρησιμοποιήσει (δηλαδή τη "μορφή" της).

🧩 Όταν Φτάσει στους "Ορισμούς"

Αργότερα, όταν "διαβάζει" τις γραμμές:

int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; }

τότε πλέον βρίσκει την υλοποίηση αυτών των συναρτήσεων — δηλαδή τι κάνουν μέσα. Ο compiler "ενώνει" αυτή την πληροφορία με όσα είχε μάθει από τα πρωτότυπα.

Η Εντολή return

📋 Χρήση της return

Η εντολή return χρησιμοποιείται για:

  • Να τερματίσει τη συνάρτηση
  • Να επιστρέψει μια τιμή στο σημείο που κλήθηκε η συνάρτηση

⚠️ Εναλλακτικός Τρόπος: Ορισμοί Συναρτήσεων Πριν από τη main()

Μπορείς επίσης να ορίσεις τις συναρτήσεις πριν από τη main() αντί να χρησιμοποιήσεις πρωτότυπα. Έτσι, ο compiler τις "βλέπει" ήδη όταν φτάσει στη main() και δεν χρειάζεται να δηλώσεις πρωτότυπα.

📘 Παράδειγμα - Ορισμοί Συναρτήσεων Πριν από τη main()

#include <stdio.h> // Χρειαζόμαστε τη βιβλιοθήκη stdio.h για τη printf(). // Πρώτα ορίζουμε τις συναρτήσεις μας. // Συνάρτηση add: επιστρέφει το άθροισμα δύο ακεραίων int add(int a, int b) { return a + b; } // Συνάρτηση multiply: επιστρέφει το γινόμενο δύο ακεραίων int multiply(int a, int b) { return a * b; } // Μετά ορίζουμε τη συνάρτηση main() int main() { int x = 10, y = 5; int sum, product; // Εδώ μπορούμε να καλέσουμε άφοβα τις add() και multiply() // γιατί ο compiler τις έχει ήδη "δει" πιο πάνω. sum = add(x, y); product = multiply(x, y); printf("x = %d, y = %d\n", x, y); printf("Άθροισμα: %d\n", sum); printf("Γινόμενο: %d\n", product); return 0; }

📘 Παράδειγμα 6 - Εύρεση Μέγιστου

#include <stdio.h> // Συνάρτηση που βρίσκει το μέγιστο από 3 αριθμούς int findMax(int a, int b, int c); int main() { int num1 = 15, num2 = 42, num3 = 28; int max; // Κλήση συνάρτησης max = findMax(num1, num2, num3); printf("Ο μέγιστος αριθμός είναι: %d\n", max); return 0; } int findMax(int a, int b, int c) { int max = a; if (b > max) { max = b; } if (c > max) { max = c; } return max; }

5.4 Συνδυασμός Πινάκων και Συναρτήσεων

📘 Παράδειγμα 7 - Συναρτήσεις με Πίνακες

#include <stdio.h> // Πρωτότυπα συναρτήσεων void printArray(int arr[], int size); int sumArray(int arr[], int size); float averageArray(int arr[], int size); int main() { int numbers[] = {10, 20, 30, 40, 50}; int size = 5; printf("Ο πίνακας: "); printArray(numbers, size); printf("Άθροισμα: %d\n", sumArray(numbers, size)); printf("Μέσος όρος: %.2f\n", averageArray(numbers, size)); return 0; } // Εμφανίζει τα στοιχεία του πίνακα void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } // Υπολογίζει το άθροισμα των στοιχείων int sumArray(int arr[], int size) { int sum = 0; for (int i = 0; i < size; i++) { sum += arr[i]; } return sum; } // Υπολογίζει το μέσο όρο των στοιχείων float averageArray(int arr[], int size) { return (float)sumArray(arr, size) / size; }

✅ Καλές Πρακτικές

  • Χρησιμοποίησε περιγραφικά ονόματα για συναρτήσεις
  • Κάθε συνάρτηση να εκτελεί μία συγκεκριμένη εργασία
  • Δήλωσε πάντα τα πρωτότυπα πριν τη χρήση
  • Χρησιμοποίησε σχόλια για να εξηγήσεις τη λειτουργία

🚨 Συνηθισμένα Λάθη

  • Πρόσβαση εκτός ορίων πίνακα
  • Ξέχασμα της εντολή return σε συναρτήσεις που επιστρέφουν τιμή
  • Λάθος αντιστοίχιση τύπων στις παραμέτρους

5.5 Μαθηματικές Συναρτήσεις από <math.h>

🧮 Έτοιμες Μαθηματικές Συναρτήσεις

Η βιβλιοθήκη math.h παρέχει πολλές έτοιμες συναρτήσεις για αριθμητικές πράξεις, όπως τετραγωνικές ρίζες, δυνάμεις, ημίτονα κ.ά.

📘 Παράδειγμα - Μαθηματικές Συναρτήσεις από math.h

#include <stdio.h> #include <math.h> int main() { double a = 9.0; double b = 2.0; printf("Η τετραγωνική ρίζα του %.2f είναι %.2f\n", a, sqrt(a)); printf("Το %.2f στη δύναμη %.2f είναι %.2f\n", a, b, pow(a, b)); return 0; }

🧮 Επεξήγηση Συναρτήσεων

  • sqrt(x) → επιστρέφει √x (τετραγωνική ρίζα).
  • pow(x, y) → επιστρέφει xy (x στη δύναμη y).

💡 Σημαντική Σημείωση για τη Μεταγλώττιση

Για να δουλέψει σωστά σε όλους τους μεταγλωττιστές, όταν το κάνεις compile γράφεις:

gcc myprog.c -lm

(το -lm λέει στο linker να συμπεριλάβει τη βιβλιοθήκη math).

5.6 Τυχαίοι Αριθμοί με <stdlib.h>

🎲 Παραγωγή Τυχαίων Αριθμών

Η βιβλιοθήκη stdlib.h παρέχει συναρτήσεις για την παραγωγή τυχαίων αριθμών, που είναι χρήσιμες σε παιχνίδια, προσομοιώσεις και πολλές άλλες εφαρμογές.

📘 Παράδειγμα - Τυχαίοι Αριθμοί

#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { srand(time(NULL)); // Αρχικοποιεί τον "γεννήτορα" τυχαίων αριθμών int r = rand() % 100; // Τυχαίος αριθμός 0–99 printf("Τυχαίος αριθμός: %d\n", r); return 0; }

🎲 Επεξήγηση Συναρτήσεων

  • rand() → παράγει τυχαίο αριθμό.
  • srand(seed) → "ορίζει" τη βάση ώστε τα αποτελέσματα να διαφέρουν κάθε φορά.

5.7 Κώδικας ASCII και Συμβολοσειρές

🎯 Στόχος

Να κατανοήσεις:

  1. Τι είναι ο κώδικας ASCII
  2. Πώς αποθηκεύονται οι χαρακτήρες και οι συμβολοσειρές στη C
  3. Πώς μπορούμε να δουλέψουμε με αυτούς στον κώδικα

Ο Κώδικας ASCII

🔠 Τι είναι ο ASCII;

Ο ASCII (American Standard Code for Information Interchange) είναι ένας πίνακας που αντιστοιχεί κάθε χαρακτήρα (γράμμα, αριθμό, σύμβολο) σε έναν αριθμό.

Για παράδειγμα:

Χαρακτήρας Τιμή ASCII
'A' 65
'a' 97
'0' 48
' ' (κενό) 32

➡️ Στη C, κάθε χαρακτήρας (char) αποθηκεύεται ως ακέραιος αριθμός 0–127 (σύμφωνα με τον πίνακα ASCII).

Παράδειγμα με Χαρακτήρες

📘 Παράδειγμα - Εμφάνιση Χαρακτήρα και ASCII Κώδικα

#include <stdio.h> int main() { char ch = 'A'; printf("Ο χαρακτήρας είναι: %c\n", ch); printf("Ο ASCII κώδικάς του είναι: %d\n", ch); return 0; }

🟢 Έξοδος:

Ο χαρακτήρας είναι: A Ο ASCII κώδικάς του είναι: 65

👉 Το %c εμφανίζει χαρακτήρα, ενώ το %d εμφανίζει τον αριθμό του (τον κώδικα ASCII).

Συμβολοσειρές στη C

🧵 Τι είναι μια Συμβολοσειρά;

Μια συμβολοσειρά (string) είναι πίνακας χαρακτήρων που τελειώνει με τον χαρακτήρα '\0' (τον "τερματιστή").

📘 Παράδειγμα - Βασική Συμβολοσειρά

#include <stdio.h> int main() { char name[] = "Hello"; printf("Η συμβολοσειρά είναι: %s\n", name); return 0; }

🟢 Έξοδος:

Η συμβολοσειρά είναι: Hello

Ο compiler το αποθηκεύει ως: 'H' 'e' 'l' 'l' 'o' '\0'

📝 Τι Συμβαίνει με τη Γραμμή

Η γραμμή:

char name[] = "Hello";

δημιουργεί έναν πίνακα χαρακτήρων στη C.

Αναλυτικά Τι Συμβαίνει

Η συμβολοσειρά "Hello" αποθηκεύεται ως εξής στη μνήμη:

Θέση Περιεχόμενο ASCII
0 'H' 72
1 'e' 101
2 'l' 108
3 'l' 108
4 'o' 111
5 '\0' 0

Δηλαδή, η συμβολοσειρά έχει 6 χαρακτήρες, γιατί στο τέλος υπάρχει ο ειδικός τερματιστής '\0' που δείχνει πού τελειώνει το string.

🧩 Ισοδύναμος Τρόπος Γραφής

Μπορούμε να γράψουμε το ίδιο πράγμα και έτσι:

char name[] = {'H', 'e', 'l', 'l', 'o', '\0'};

Αυτό κάνει ακριβώς το ίδιο με το "Hello". Απλώς η πρώτη μορφή ("Hello") είναι πιο σύντομη και συνηθισμένη.

🧠 Θυμήσου:

  • Κάθε συμβολοσειρά στη C είναι πίνακας char.
  • Πρέπει πάντα να τελειώνει με '\0', αλλιώς η εκτύπωση με %s θα συνεχίσει να διαβάζει "σκουπίδια" από τη μνήμη.

Πώς Αποθηκεύεται στη Μνήμη

Ας δούμε πώς φαίνεται ο πίνακας name στη μνήμη, byte προς byte, όταν γράφουμε:

char name[] = "Hello";

🧩 Αναπαράσταση στη Μνήμη

Ας πούμε ότι η μεταβλητή name ξεκινά στη διεύθυνση 1000 (ενδεικτικά). Η C θα αποθηκεύσει κάθε χαρακτήρα σε ένα byte (1 byte = 8 bits).

Διεύθυνση (παράδειγμα) Περιεχόμενο ASCII Περιγραφή
1000 'H' 72 Πρώτος χαρακτήρας
1001 'e' 101 Δεύτερος χαρακτήρας
1002 'l' 108 Τρίτος χαρακτήρας
1003 'l' 108 Τέταρτος χαρακτήρας
1004 'o' 111 Πέμπτος χαρακτήρας
1005 '\0' 0 Τερματιστής συμβολοσειράς

📘 Τι Σημαίνει Αυτό

  • Ο δείκτης name δείχνει στη διεύθυνση του πρώτου χαρακτήρα (&name[0]).
  • Όταν γράφεις printf("%s", name);, η C ξεκινά να τυπώνει χαρακτήρες μέχρι να βρει το '\0'.
  • Αν λείπει το '\0', η C θα συνεχίσει να τυπώνει άγνωστα bytes από τη μνήμη → "σκουπίδια" (garbage).

🔍 Παράδειγμα για να το Δεις στην Πράξη

#include <stdio.h> int main() { char name[] = "Hello"; for (int i = 0; i < 6; i++) { printf("Διεύθυνση %p -> χαρακτήρας '%c' (ASCII: %d)\n", &name[i], name[i], name[i]); } return 0; }

🟢 Παράδειγμα Εξόδου:

Διεύθυνση 0x7ffee9c1d7f0 -> χαρακτήρας 'H' (ASCII: 72) Διεύθυνση 0x7ffee9c1d7f1 -> χαρακτήρας 'e' (ASCII: 101) Διεύθυνση 0x7ffee9c1d7f2 -> χαρακτήρας 'l' (ASCII: 108) Διεύθυνση 0x7ffee9c1d7f3 -> χαρακτήρας 'l' (ASCII: 108) Διεύθυνση 0x7ffee9c1d7f4 -> χαρακτήρας 'o' (ASCII: 111) Διεύθυνση 0x7ffee9c1d7f5 -> χαρακτήρας ' ' (ASCII: 0)

🔸 Οι διευθύνσεις διαφέρουν κάθε φορά που τρέχεις το πρόγραμμα, αλλά μπορείς να δεις ότι κάθε χαρακτήρας πιάνει 1 byte και είναι σε συνεχόμενη διεύθυνση στη μνήμη.

ASCII Τιμές Μέσα σε Συμβολοσειρές

📘 Παράδειγμα - Εμφάνιση ASCII Τιμών Συμβολοσειράς

#include <stdio.h> int main() { char word[] = "Hi!"; for (int i = 0; word[i] != '\0'; i++) { printf("Χαρακτήρας: %c - ASCII: %d\n", word[i], word[i]); } return 0; }

🟢 Έξοδος:

Χαρακτήρας: H - ASCII: 72 Χαρακτήρας: i - ASCII: 105 Χαρακτήρας: ! - ASCII: 33

Μικρή Άσκηση

🧠 Άσκηση

✏️ Άσκηση: Γράψε ένα πρόγραμμα που θα διαβάζει έναν χαρακτήρα από το χρήστη και θα εμφανίζει τον ASCII κώδικά του.

🧩 Υπόδειξη:

  • Χρησιμοποίησε scanf("%c", &ch);
  • Εκτύπωσε τον ASCII αριθμό με %d

🎓 Τέλος Κεφαλαίου 5

Συγχαρητήρια! Τώρα ξέρεις:

  • ✓ Πώς να δημιουργείς και να χρησιμοποιείς πίνακες
  • ✓ Πώς να δουλεύεις με δισδιάστατους πίνακες
  • ✓ Πώς να δημιουργείς και να καλείς συναρτήσεις
  • ✓ Πώς να συνδυάζεις πίνακες με συναρτήσεις

Μπάρδης Γιώργος

Τμήμα Ψηφιακών Συστημάτων - Πανεπιστήμιο Πελοποννήσου