Στόχοι: μετά από αυτό το κεφάλαιο θα μπορείτε να χρησιμοποιείτε πολυδιάστατους πίνακες και να υπολογίζετε πού βρίσκεται στη μνήμη το
a[i][j]· να χειρίζεστε δείκτες σε δείκτες, πίνακες από δείκτες και τοargv· να δεσμεύετε δυναμικούς πίνακες μεmalloc· και να εξηγείτε τι είναι το endianness.Προαπαιτούμενα: Κεφάλαιο 10, Κεφάλαιο 11
Χρόνος μελέτης: ~2,5 ώρες
Η διάλεξη πηγαίνει τους πίνακες και τους δείκτες ένα βήμα πιο πέρα. Οι δισδιάστατοι
πίνακες της C είναι πίνακες από πίνακες, αποθηκευμένοι γραμμή-γραμμή σε συνεχόμενη
μνήμη, οπότε η θέση του a[i][j] βγαίνει με έναν απλό τύπο. Οι δείκτες σε δείκτες
και οι πίνακες από δείκτες (με κορυφαίο παράδειγμα το argv) προσθέτουν ένα επίπεδο
έμμεσης αναφοράς. Η malloc δίνει πίνακες με μέγεθος που αποφασίζεται κατά την
εκτέλεση, και το endianness εξηγεί τη σειρά των bytes ενός ακεραίου. Όλα αυτά
στηρίζουν τις δομές δεδομένων του δεύτερου μισού του μαθήματος.
Ένας πίνακας (array) κρατά δεδομένα ίδιου τύπου (Κεφάλαιο 10).
Στη δήλωση int bears[100]; ο τύπος (type) ορίζει πόση μνήμη παίρνει κάθε
στοιχείο, το όνομα (name) κάνει τον μεταγλωττιστή να διαλέξει μια διεύθυνση για
τον πίνακα, και το μέγεθος (size) πόσα στοιχεία θα κρατήσει. Το μέγεθος είναι
στατικό: δεν αλλάζει κατά την εκτέλεση. Στα στοιχεία αναφερόμαστε με τη θέση
(index) τους, bears[0] έως bears[99], και αυτά κάθονται σε συνεχόμενες θέσεις.
Με sizeof(int) == 4 και αρχή στη διεύθυνση 4:
| Bytes | 0–3 | 4–7 | 8–11 | … | 400–403 |
|---|---|---|---|---|---|
| Περιεχόμενο | (άλλο) | bears[0] |
bears[1] |
… | bears[99] |
Ο πίνακας πιάνει \(4 \cdot 100 = 400\) bytes (4–403), και το bears[i] βρίσκεται στη
διεύθυνση «αρχή + i * sizeof(int)».
Εικόνες, επιστημονικά και οικονομικά δεδομένα ή μια σκακιέρα έχουν φυσικά
περισσότερες διαστάσεις (dimensions). Ένας δισδιάστατος πίνακας
(two-dimensional array) είναι πίνακας από (υπο)πίνακες:
τύπος όνομα[γραμμές][στήλες];. Οι γραμμές (rows) είναι πόσους υποπίνακες έχει
(1η διάσταση) και οι στήλες (columns) πόσα στοιχεία έχει ο καθένας (2η διάσταση),
άρα συνολικά γραμμές × στήλες στοιχεία. Το a[i][j] είναι το στοιχείο της γραμμής
i και της στήλης j, με μέτρηση από το 0, και χρησιμοποιείται όπως μια απλή
μεταβλητή. Η αρχικοποίηση δίνει μία λίστα σε αγκύλες ανά γραμμή (εδώ η γραμμή 0
είναι 1, 4, 7, 10 και η γραμμή 1 είναι 3, 6, 9, 12):
int array[2][4] = {
{1, 4, 7, 10},
{3, 6, 9, 12},
};
Σε αρχικοποίηση μπορεί να παραλειφθεί μόνο το μέγεθος της πρώτης διάστασης
(char arr[][2] = {{'a','b'},{'c','d'}};, από τις σημειώσεις)· ο λόγος φαίνεται
στον υπολογισμό διευθύνσεων παρακάτω.
Η μνήμη είναι μονοδιάστατη, γι’ αυτό η C αποθηκεύει τον πίνακα κατά γραμμές
(row-major order): όλη η γραμμή 0, αμέσως μετά η γραμμή 1. Για τον παραπάνω
array, με αρχή στο 100 (κάθε στοιχείο πιάνει 4 bytes):
| Bytes | 100 | 104 | 108 | 112 | 116 | 120 | 124 | 128 |
|---|---|---|---|---|---|---|---|---|
| Τιμή | 1 | 4 | 7 | 10 | 3 | 6 | 9 | 12 |
| Στοιχείο | [0][0] |
[0][1] |
[0][2] |
[0][3] |
[1][0] |
[1][1] |
[1][2] |
[1][3] |
Αφού ο πίνακας είναι πίνακας από πίνακες, το array[0] είναι από μόνο του ένας
πίνακας 4 ακεραίων (bytes 100–115) και το array[1] ο επόμενος (116–131). Γι’ αυτό
sizeof(array[0]) και sizeof(array[1]) τυπώνουν 16 και sizeof(array) 32 (οι
διαφάνειες τυπώνουν με %d· για size_t το σωστό είναι %zu), και
sizeof(array) / sizeof(array[0]) δίνει το πλήθος των γραμμών.
Για int array[X][Y];, για να φτάσουμε στο a[i][j] προσπερνάμε i γραμμές των Y
ακεραίων και μετά j ακεραίους:
&a[i][j] = StartAddressOfArray + i * Y * sizeof(int) + j * sizeof(int)
IndexOfElementIJ = i * Y + j
Ο δεύτερος είναι η θέση σε έναν νοητό μονοδιάστατο πίνακα X * Y ακεραίων. Και οι δύο χρειάζονται μόνο τον αριθμό Y των στηλών. Γι’ αυτό
η πρώτη διάσταση μπορεί να παραλειφθεί, και μια συνάρτηση που δέχεται δισδιάστατο
πίνακα γράφει την παράμετρο π.χ. int matr[][12] (σημειώσεις, «Πολυδιάστατοι
πίνακες»). Έλεγχος με τον πίνακα παραπάνω: το a[1][2] είναι στο
\(100 + 1 \cdot 4 \cdot 4 + 2 \cdot 4 = 124\), όπου πράγματι βρίσκεται το 9.
Πίνακες τριών ή περισσότερων διαστάσεων λειτουργούν με τον ίδιο τρόπο, με περισσότερα
ζεύγη αγκυλών (π.χ. int rubiksCube[6][3][3];), και είναι πάλι συνεχόμενοι στη μνήμη:
για [X][Y][Z] η θέση του [i][j][k] είναι i * Y * Z + j * Z + k. Η διάλεξη ρωτά:
είναι απαραίτητοι οι πολυδιάστατοι πίνακες; Όχι· μπορούμε να κρατήσουμε τα ίδια
δεδομένα σε μονοδιάστατο πίνακα και να υπολογίζουμε μόνοι μας τη θέση. Είναι όμως
βολικοί, γιατί τον λογαριασμό τον κάνει ο μεταγλωττιστής.
Ένας δείκτης (pointer) κρατά μια διεύθυνση μνήμης
(Κεφάλαιο 11). Ο μοναδιαίος * κάνει αποαναφορά
(dereference): μετά το int *pointer = &x; το *pointer είναι ισοδύναμο με το
x, για ανάγνωση και για εγγραφή (αν το x είναι στη διεύθυνση 100, ο pointer
περιέχει την τιμή 100).
Στην αριθμητική δεικτών (pointer arithmetic), αν ο p δείχνει σε int, ο p + 2
δείχνει δύο ακεραίους (όχι bytes) πιο μετά, το *(p + 2) ισοδυναμεί με p[2] και το
p++ πάει στο επόμενο στοιχείο. Το όνομα ενός πίνακα σε έκφραση μετατρέπεται σε
δείκτη στο πρώτο του στοιχείο, άρα το p = x; κάνει τον p να δείχνει στο x[0].
Ένας δείκτης έχει κι αυτός διεύθυνση, που μπορεί να αποθηκευτεί σε άλλον δείκτη. Ο
δείκτης σε δείκτη (pointer to pointer) δηλώνεται με δύο αστερίσκους: μετά από
int x = 42; int *ptr = &x; int **ptr2 = &ptr; ο ptr2 είναι δείκτης σε int *
(διαβάστε τον τύπο από δεξιά). Με τις διευθύνσεις της διαφάνειας:
| Μεταβλητή | Τύπος | Διεύθυνση | Τιμή |
|---|---|---|---|
x |
int |
100 | 42 |
ptr |
int * |
200 | 100 |
ptr2 |
int ** |
400 | 200 |
flowchart LR
P2["ptr2: int ** (400)"] --> P["ptr: int * (200)"]
P --> X["x: int (100) = 42"]
Σχήμα: κάθε * ακολουθεί ένα βέλος· *ptr2 είναι ο ptr (100), **ptr2 ο x (42).
Η διάλεξη ρωτά τι τυπώνει το printf("%p %d", *ptr2, **ptr2);: μια διεύθυνση (σε
δεκαεξαδικό, διαφορετική σε κάθε εκτέλεση) και το 42. Το ίδιο γενικεύεται σε int ***.
Γιατί έχει σημασία; Δέντρα και γράφοι είναι κόμβοι που δείχνουν σε άλλους κόμβους
(Κεφάλαιο 21), και το argv και οι δυναμικοί δισδιάστατοι
πίνακες έχουν τύπους char ** και int **.
Οι δείκτες μπαίνουν και σε πίνακες: ο πίνακας από δείκτες (array of pointers)
int *ptr[3]; έχει τρία στοιχεία τύπου int *. Το *ptr[2] σημαίνει *(ptr[2]),
γιατί οι αγκύλες έχουν μεγαλύτερη προτεραιότητα από τον μοναδιαίο *.
Η συνηθέστερη χρήση είναι ένας πίνακας συμβολοσειρών, char *sentence[]: κάθε
στοιχείο δείχνει στον πρώτο χαρακτήρα μιας συμβολοσειράς, και σε αντίθεση με έναν
δισδιάστατο πίνακα char οι συμβολοσειρές μπορούν να έχουν διαφορετικά μήκη. Συχνά
το τελευταίο στοιχείο είναι ο κενός δείκτης (null pointer) NULL, που λειτουργεί
ως «φρουρός»: ένας βρόχος με συνθήκη sentence[i] σταματά εκεί χωρίς να ξέρει το
πλήθος.
flowchart LR
S0["sentence[0]"] --> W0["I'm"]
S1["sentence[1]"] --> W1["singing"]
S2["sentence[2]"] --> W2["in"]
S3["sentence[3]"] --> W3["the"]
S4["sentence[4]"] --> W4["rain"]
S5["sentence[5]"] --> W5["!"]
S6["sentence[6]"] --> N["NULL"]
Σχήμα: πίνακας από δείκτες σε συμβολοσειρές διαφορετικού μήκους, με NULL στο τέλος.
Το πιο γνωστό παράδειγμα πίνακα από δείκτες είναι η παράμετρος της main για τα
ορίσματα γραμμής εντολών (command-line arguments):
int main(int argc, char * argv[]). Το argc είναι το πλήθος των ορισμάτων μαζί με
το όνομα του προγράμματος, και το argv[i] δείχνει στη συμβολοσειρά του i-οστού
ορίσματος. Για ./echo hello fine world έχουμε argc = 4:
flowchart LR
A0["argv[0]"] --> T0["'.' '/' 'e' 'c' 'h' 'o' '#92;0'"]
A1["argv[1]"] --> T1["'h' 'e' 'l' 'l' 'o' '#92;0'"]
A2["argv[2]"] --> T2["'f' 'i' 'n' 'e' '#92;0'"]
A3["argv[3]"] --> T3["'w' 'o' 'r' 'l' 'd' '#92;0'"]
Σχήμα: το argv για ./echo hello fine world (argc = 4).
Τα πραγματικά ορίσματα ξεκινούν από το argv[1], και το argv[argc] είναι NULL
(σημειώσεις, «Ορίσματα γραμμής εντολών»). Επειδή ένας πίνακας περνά σε συνάρτηση ως
δείκτης στο πρώτο του στοιχείο, η παράμετρος γράφεται ισοδύναμα char **argv.
Όταν το μέγεθος γίνεται γνωστό μόνο κατά την εκτέλεση (π.χ. διαβάζεται από την είσοδο), χρησιμοποιούμε δυναμικούς πίνακες (dynamic arrays):
τύπος * όνομα = malloc(μέγεθος * sizeof(τύπος));
Η malloc (από το stdlib.h) δεσμεύει μνήμη και επιστρέφει τη διεύθυνσή της, που
την κρατά ένας δείκτης στον τύπο των στοιχείων. Το όρισμα είναι σε bytes, γι’ αυτό
πολλαπλασιάζουμε το πλήθος με sizeof(τύπος). Το
int * array = malloc(N * sizeof(int)); δημιουργεί πίνακα N ακεραίων, που χάρη στην
αριθμητική δεικτών χρησιμοποιείται κανονικά, από array[0] έως array[N - 1]. Η
μνήμη αυτή βρίσκεται στον σωρό (heap), το θέμα του Κεφαλαίου 13.
Από τις σημειώσεις: η malloc δεν αρχικοποιεί τη μνήμη (οι τιμές 1, 3, 3, 7 της
διαφάνειας είναι ενδεικτικές)· αν αποτύχει επιστρέφει NULL, οπότε ελέγχουμε πάντα
το αποτέλεσμα· και ό,τι δεσμεύσαμε το αποδεσμεύουμε με free. Το sizeof ενός
δείκτη είναι το μέγεθος μιας διεύθυνσης (8 bytes σε σύστημα 64 bit), όχι του μπλοκ,
οπότε το πλήθος των στοιχείων το κρατάμε σε μεταβλητή. Ένας δυναμικός δισδιάστατος
πίνακας N × M φτιάχνεται με έναν int ** που δείχνει σε N δείκτες γραμμών, με
κάθε γραμμή από δική της malloc, ή με έναν μονοδιάστατο πίνακα N * M και τη θέση
i * M + j.
Ένας int αποτελείται από πολλά bytes. Endianness λέμε τη σειρά με την οποία
αποθηκεύονται: little endian από το μικρότερο (λιγότερο σημαντικό) byte προς το
μεγαλύτερο, big endian αντίστροφα. Για τα διαδοχικά bytes 68 65 6c 6c (ASCII
'h' 'e' 'l' 'l'):
| Μηχάνημα | byte +0 | byte +1 | byte +2 | byte +3 | Ακέραιος |
|---|---|---|---|---|---|
| little endian | 68 |
65 |
6c |
6c |
0x6c6c6568 |
| big endian | 68 |
65 |
6c |
6c |
0x68656c6c |
Οι x86 και οι περισσότεροι ARM είναι little endian (τα σχήματα μνήμης των διαφανειών για τους δείκτες είναι σχεδιασμένα big endian, που διαβάζεται ευκολότερα). Το endianness μετράει σε δυαδικά δεδομένα, π.χ. κεφαλίδες αρχείων εικόνας και ήχου.
Εφαρμόζει τους «Δισδιάστατους πίνακες»: κάθε κελί είναι ένας char (κεφαλαία για τον
έναν παίκτη, πεζά για τον άλλο, κενό για άδειο τετράγωνο).
#include <stdio.h>
int main() {
char chessBoard[8][8] = {
{'R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'},
{'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'},
{'r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'}
};
chessBoard[1][4] = ' ';
chessBoard[3][4] = 'p';
return 0;
}
Οι δύο αναθέσεις κάνουν την κίνηση e2–e4 (γραμμή 1 → γραμμή 3, στήλη 4). Με τη
σύμβαση του πίνακα το πιόνι θα έπρεπε να είναι 'P'· η διαφάνεια γράφει 'p'. Για
εκτύπωση, ο εξωτερικός βρόχος διατρέχει τις γραμμές και ο εσωτερικός τις στήλες.
Εφαρμόζει τους «Πίνακες περισσότερων διαστάσεων»: 6 έδρες × 3 × 3 = 54 ακέραιοι.
int rubiksCube[6][3][3] = {
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, // Face 1 (e.g., Red)
{{1, 1, 1}, {1, 1, 1}, {1, 1, 1}}, // Face 2 (e.g., Green)
{{2, 2, 2}, {2, 2, 2}, {2, 2, 2}}, // Face 3 (e.g., Blue)
{{3, 3, 3}, {3, 3, 3}, {3, 3, 3}}, // Face 4 (e.g., Yellow)
{{4, 4, 4}, {4, 4, 4}, {4, 4, 4}}, // Face 5 (e.g., Orange)
{{5, 5, 5}, {5, 5, 5}, {5, 5, 5}} // Face 6 (e.g., White)
};
Εφαρμόζει τη «Δείκτες: υπενθύμιση». Ποια είναι τα περιεχόμενα του x μετά την
εκτέλεση;
int * p;
int x[] = {5, 7, 2, 3, 6, 0, 1, 4};
p = x;
while (*p = *(p+2))
p++;
Η συνθήκη είναι ανάθεση: αντιγράφει στο *p την τιμή δύο θέσεις πιο μετά, και
αφού η τιμή μιας ανάθεσης είναι η τιμή που ανατέθηκε, ο βρόχος σταματά μόλις
αντιγραφεί ένα 0. Ο πίνακας αλλάζει όσο τρέχει ο βρόχος, οπότε ιχνηλατήστε βήμα-βήμα.
Για όσους δυσκολεύονται με τους δείκτες, η διάλεξη προτείνει το βίντεο των βοηθών
(δείτε το Διάβασμα).
Εφαρμόζει τους «Πίνακες από δείκτες»:
#include <stdio.h>
int main() {
int *ptr[3], a = 100, b = 200, c = 300;
ptr[0] = &a;
ptr[1] = &b;
ptr[2] = &c;
printf("%d %d %d\n", *ptr[2], *ptr[1], *ptr[0]);
return 0;
}
$ ./example
300 200 100
Εφαρμόζει τους «Πίνακες από δείκτες» με φρουρό NULL:
#include <stdio.h>
int main() {
char *sentence[] = {
"I'm", "singing", "in", "the", "rain", "!", NULL
};
int i;
for(i = 0 ; sentence[i]; i++) {
printf("%s\n", sentence[i]);
}
return 0;
}
$ ./sentence
I'm
singing
in
the
rain
!
Εφαρμόζει «Το argv»: τυπώνει όλα τα ορίσματα, μαζί με το όνομα του προγράμματος. Για
ορίσματα-αριθμούς (π.χ. argcalc.c του εργαστηρίου 8) χρειάζεται η atoi.
#include <stdio.h>
int main(int argc, char * argv[]) {
int i;
for(i = 0 ; i < argc ; i++) {
printf("%s\n", argv[i]);
}
return 0;
}
$ ./echo hello fine world
./echo
hello
fine
world
Εφαρμόζει τους «Δυναμικούς πίνακες». Πόση μνήμη δεσμεύει κάθε κλήση και τι τυπώνει το
printf;
int * nums = malloc(100 * sizeof(int));
double * coeffs = malloc(100 * sizeof(double));
char * str = malloc(100 * sizeof(char));
printf("%zu %zu %zu\n", sizeof(nums), sizeof(coeffs), sizeof(str));
$ ./dynamic
8 8 8
Κάθε κλήση δεσμεύει 100 * sizeof(τύπος) bytes, αλλά το printf τυπώνει το μέγεθος
των ίδιων των δεικτών: 8 bytes ο καθένας σε σύστημα 64 bit. Για την άσκηση array.c
του εργαστηρίου 7: διαβάστε το N, δεσμεύστε, ελέγξτε για NULL, και στο τέλος
free.
Εφαρμόζει το «Endianness». Μετατρέπουμε (cast) τη διεύθυνση του x σε char *, που
προχωρά ένα byte τη φορά. Τι θα τυπώσει;
#include <stdio.h>
int main() {
int x = 42;
char * bytes = (char*)&x;
int i;
for(i = 0; i < sizeof(int) / sizeof(char); i++)
printf("%02x\n", bytes[i]);
return 0;
}
$ ./int
2a
00
00
00
Το 42 είναι 0x0000002a· πρώτο τυπώνεται το λιγότερο σημαντικό byte, άρα το
μηχάνημα είναι little endian. Επειδή το char είναι συνήθως προσημασμένο, ένα byte
0x80 θα έβγαινε ffffff80 με %02x· με unsigned char * αυτό δεν συμβαίνει.
τύπος όνομα[γραμμές][στήλες],
αρχικοποιείται με μία λίστα ανά γραμμή, και το a[i][j] είναι μια απλή μεταβλητή.sizeof(array[0]) είναι μία γραμμή,
sizeof(array) όλος ο πίνακας.int array[X][Y] η θέση του a[i][j] είναι i * Y + j: χρειάζεται μόνο το Y,
γι’ αυτό μόνο η πρώτη διάσταση μπορεί να παραλειφθεί.*pointer ισοδυναμεί με τη μεταβλητή όπου δείχνει ο δείκτης· ένας int **
κρατά τη διεύθυνση ενός δείκτη, και κάθε * ακολουθεί ένα επίπεδο.char * με NULL στο τέλος κρατά
συμβολοσειρές διαφορετικού μήκους.argv είναι πίνακας από char * (ισοδύναμα char **) με argc στοιχεία, και
το argv[0] είναι το όνομα του προγράμματος.malloc(μέγεθος * sizeof(τύπος)) φτιάχνει στον σωρό πίνακα με μέγεθος που
αποφασίζεται κατά την εκτέλεση· το sizeof του δείκτη είναι 8, όχι το μέγεθος του
μπλοκ.char * το διαπιστώνουμε.| Ελληνικά | English | Σύντομος ορισμός |
|---|---|---|
| πίνακας / θέση | array / index | Στοιχεία ίδιου τύπου σε συνεχόμενη μνήμη / ο i στο a[i]. |
| δισδιάστατος πίνακας | two-dimensional array | Πίνακας από πίνακες, a[γραμμές][στήλες]. |
| αποθήκευση κατά γραμμές | row-major order | Οι γραμμές αποθηκεύονται η μία μετά την άλλη. |
| αποαναφορά | dereference | Πρόσβαση με * εκεί όπου δείχνει ένας δείκτης. |
| δείκτης σε δείκτη | pointer to pointer | Δείκτης που κρατά διεύθυνση δείκτη, π.χ. int **. |
| πίνακας από δείκτες | array of pointers | Πίνακας με στοιχεία τύπου δείκτη, π.χ. char *s[5]. |
| κενός δείκτης | null pointer (NULL) |
Δείκτης που δεν δείχνει πουθενά· συχνά σημαδεύει το τέλος. |
| ορίσματα γραμμής εντολών | command-line arguments | Οι λέξεις της κλήσης, στα argc/argv. |
| δυναμικός πίνακας | dynamic array | Πίνακας με μέγεθος που αποφασίζεται κατά την εκτέλεση. |
| σωρός | heap | Η περιοχή μνήμης από την οποία δεσμεύει η malloc. |
| σειρά bytes | endianness | Η σειρά αποθήκευσης των bytes ενός ακεραίου. |
argv 29–35· malloc 36–40·
endianness 41–43.pointers.c· Εργαστήριο 7:
twodim.c, array.c, mines.c·
Εργαστήριο 8: argcalc.c.man 3 malloc.a[i, j] αντί για a[i][j]. Το κόμμα είναι τελεστής, άρα σημαίνει a[j].int a[2][4] το a[3][1] είναι εκτός ορίων
(Segmentation fault): ο πρώτος δείκτης είναι η γραμμή.int m[][] = {{1, 2}, {3, 4}}; δίνει
array type has incomplete element type. Γράψτε int m[][2].sizeof δείκτη ως μέγεθος πίνακα (8, όχι 400) ή sizeof με %d
(format '%d' expects argument of type 'int'): κρατήστε το πλήθος σε μεταβλητή
και τυπώνετε το sizeof με %zu.sizeof ή stdlib.h στη malloc. Το malloc(100) χωρά μόνο 25
int· χωρίς #include <stdlib.h> ο gcc λέει
implicit declaration of function 'malloc'.NULL ή αρχικοποίηση. Η malloc μπορεί να αποτύχει και δεν
μηδενίζει τη μνήμη: ελέγξτε if (array == NULL) και γράψτε πριν διαβάσετε.argv[1] χωρίς έλεγχο του argc. Χωρίς όρισμα το argv[1] είναι NULL και το
atoi(argv[1]) δίνει Segmentation fault. Ελέγξτε if (argc < 2)· και θυμηθείτε
ότι για ./prog a b c το argc είναι 4.NULL. Το for (i = 0; sentence[i]; i++)
βγαίνει έξω από τον πίνακα.Από τα Kahoot των διαλέξεων: οι ερωτήσεις όπου μια λάθος απάντηση μάζεψε πολλές ψήφους, με το ποσοστό σωστών απαντήσεων.
155: μέτρησαν σωστά ότι προηγούνται 55 στοιχεία, αλλά ξέχασαν να τα πολλαπλασιάσουν με sizeof(int) για να τα κάνουν bytes."fine" και άλλο 22% 'e': οι πρώτοι σταμάτησαν στο strings[1] αγνοώντας τον δεύτερο δείκτη, οι δεύτεροι μέτρησαν τις θέσεις από το 1.1, θεωρώντας ότι το map[5] είναι ένας χαρακτήρας. Στην πραγματικότητα το map[5] είναι ολόκληρη η 6η γραμμή, ένας πίνακας 10 char.*, άρα το argv είναι πρώτα πίνακας, και τα στοιχεία του είναι char *.double m[3][5]; (sizeof(double) == 8), και πού βρίσκεται το
a[2][3] του int a[10][20]; αν αυτός ξεκινά στο 1000;1a[i][j];2./prog one two, ποιο είναι το argc και τι περιέχει το argv[0];3malloc(100 * sizeof(double)) και πόσο είναι το sizeof του
δείκτη που το κρατά;40x12345678 σε little endian;5Ερωτήσεις που παίχτηκαν στις διαλέξεις, με το ποσοστό των φοιτητών που απάντησαν σωστά.
slides-lec12-2d-elementslides-lec12-array-of-pointersslides-lec12-pointer-to-pointerslides-lec12-endiannessslides-lec12-malloc-sizeofslides-lec12-multidim-necessaryslides-lec12-pointer-copy-looplab-lab07-arraylab-lab08-argcalclab-lab07-twodimlab-lab08-legolashw-2023-hw2-fauxtoshopexam-2023-fall-ex13-q2exam-2023-fall-ex2-q2exam-2025-sep-q3exam-2023-fall-ex1-q3exam-2023-fall-ex11-q3exam-2023-fall-ex8-q3exam-2023-fall-ex9-q3exam-2024-jul-q2exam-2025-jan-q3exam-2026-sep-q3exam-2023-fall-ex8-q1exam-2023-fall-ex9-q2hw-2025-hw1-soundwaveexam-2023-fall-ex11-q2exam-2023-fall-ex3-q2exam-2023-fall-ex5-q2hw-2024-hw1-gcdhw-2024-hw1-rsalab-lab06-pointerslab-lab07-mineslab-lab07-pacmanslides-lec13-dynamic-2dexam-2023-fall-ex0-q2exam-2023-fall-ex1-q2exam-2023-fall-ex5-q4exam-2025-sep-q2lab-lab08-stringslides-lec14-check-booslides-lec15-complexity-find-max-2dslides-lec15-complexity-mallocslides-lec16-char-pointer-arraysslides-lec16-search-2dexam-2023-fall-ex10-q4exam-2026-jan-q2exam-2023-fall-ex4-q4exam-2023-fall-ex7-q4hw-2024-hw2-futurelab-lab10-countlab-lab10-filedifflab-lab10-moreslides-lec18-2d-sizeofslides-lec18-fread-intslides-lec18-scanf-stringhw-2023-hw3-zoombaexam-2024-dec-q4exam-2024-sep-q6exam-2025-sep-q5exam-2026-jan-q5