#define, μόνο που την διαχειρίζεται ο μεταγλωττιστής, όχι ο προεπεξεργαστής.Παράδειγμα:
enum boolean {NO, YES};
Για τη συνέχεια, το NO ταυτίζεται με το 0 και το YES με το 1.
Παραδείγματα:
enum months {JAN = 1, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC};
enum escapes {BELL = '\a', BACKSPACE = '\b',
TAB = '\t', NEWLINE = '\n'};
Εδώ, οι σταθερές για τους μήνες στην απαρίθμηση months αντιστοιχούν στους αριθμούς από 1 έως 12, ενώ οι σταθερές της απαρίθμησης escapes αντιστοιχούν στις συγκεκριμένες τιμές που έχουν καθορισθεί για την καθεμία.
struct ⟨ετικέτα δομής⟩ {;;;;typedef), ενώσεις και πεδία bit, που θα συζητηθούν στη συνέχεια, οι παράγραφοι από το [KR] είναι οι §\(6.7\), §\(6.8\) και §\(6.9\), αντίστοιχα.Παραδείγματα:
struct employee {
char firstname[10];
char lastname[18];
int id_number;
float salary;
};
struct wordinfo {
char *word;
int nlines;
};
struct employee για την αναπαράσταση πληροφοριών για ένα υπάλληλο (μικρό όνομα, επώνυμο, αριθμός μητρώου και μισθός) και μία δομή struct wordinfo για την αναπαράσταση της πληροφορίας σχετικά με την ύπαρξη μίας λέξης σε κάποιο κείμενο, καθώς και το πλήθος των γραμμών στις οποίες εμφανίζεται.struct ⟨ετικέτα δομής⟩ ⟨μεταβλητή⟩\(_1\), …, ⟨μεταβλητή⟩\(_k\);Παραδείγματα:
struct employee jim, jane, the_employee;
struct wordinfo first_word, next_word, last_word;
Άλλα παραδείγματα δηλώσεων δομών:
struct point {
double x;
double y;
};
struct upright_rectangle {
struct point p1;
struct point p2;
};
struct point για την αναπαράσταση ενός σημείου στο επίπεδο (μέσω των συντεταγμένων του), καθώς και τη δομή struct upright_rectangle για την αναπαράσταση ενός ορθογωνίου παραλληλογράμμου στο επίπεδο, με τις πλευρές παράλληλες στους άξονες (μέσω δύο σημείων που είναι απέναντι κορυφές σε μία διαγώνιο). 1Ο ορισμός μεταβλητών με τύπο κάποια συγκεκριμένη δομή μπορεί να γίνει ταυτόχρονα με τη δήλωση της δομής. Παράδειγμα:
struct point {
double x;
double y;
} pa, pb, pc;
Στην περίπτωση αυτή, δεν είναι απαραίτητο να δώσουμε ετικέτα στη δομή. Για παράδειγμα, ο εξής ορισμός των μεταβλητών pa, pb και pc είναι αποδεκτός:
struct {
double x;
double y;
} pa, pb, pc;
Φυσικά, τότε, αν θέλουμε στη συνέχεια να ορίσουμε και άλλες μεταβλητές του τύπου της δομής, πρέπει να επαναλάβουμε τη δήλωσή της, αφού δεν της είχαμε δώσει κάποια ετικέτα.
Με τον ορισμό μίας μεταβλητής τύπου δομής, μπορούμε να κάνουμε και αρχικοποίηση των μελών της. Παράδειγμα:
struct point my_point = {22.4, -38.9};
Για αυτόματες μεταβλητές τύπου δομής, αλλά και προκαθορισμένων τύπων, αρχικοποίηση μπορεί να γίνει και με εντολή αντικατάστασης ή μέσω επιστροφής συνάρτησης.
.⟨μέλος⟩Παραδείγματα:
struct point vert1, vert2;
struct upright_rectangle my_rect;
vert1.x = 2.4;
vert2.y = 7.8;
my_rect.p1.x = -8.3;
Η τελευταία εντολή είναι ισοδύναμη με την
(my_rect.p1).x = -8.3;
επειδή ο τελεστής . είναι αριστερά προσεταιριστικός.
Όπως ορίζουμε μεταβλητές με τύπο κάποια δομή, έτσι μπορούμε να ορίσουμε και δείκτες σε δομές. Παράδειγμα:
struct point *ppa, *ppb;
Φυσικά, είναι δική μας ευθύνη να κάνουμε τους δείκτες ppa και ppb να δείξουν σε διευθύνσεις στις οποίες φυλάσσονται ή θα φυλαχθούν δεδομένα τύπου δομής. Αυτό μπορεί να γίνει είτε με την ανάθεση της διεύθυνσης κάποιας ήδη ορισμένης μεταβλητής τύπου δομής, είτε με δυναμική δέσμευση. Παραδείγματα:
struct point my_point;
ppa = &my_point;
ppb = malloc(sizeof(struct point));
ppa στο προηγούμενο παράδειγμα, μπορούμε να αναφερθούμε σε συγκεκριμένο μέλος κατά τα γνωστά, για παράδειγμα (*ppa).x.(*ppa).x οι παρενθέσεις είναι απαραίτητες. Αν γράφαμε *ppa.x, αυτό, λόγω των σχετικών προτεραιοτήτων των τελεστών . και * θα ήταν ισοδύναμο με το *(ppa.x), το οποίο όμως, στην προκείμενη περίπτωση δεν είναι συντακτικά σωστό, αφού το ppa.x δεν είναι δείκτης.(*⟨δείκτης⟩).⟨μέλος⟩->⟨μέλος⟩Όπως ο τελεστής ., έτσι και ο -> είναι αριστερά προσεταιριστικός. Για παράδειγμα, αν έχουμε ορίσει
struct upright_rectangle *rp;
τότε η παράσταση rp->p1.x είναι η ίδια με την (rp->p1).x.
. και -> μαζί με τις () για κλήσεις συναρτήσεων και τις [] για δείκτες πινάκων βρίσκονται στην κορυφή της ιεραρχίας προτεραιότητας των τελεστών και επομένως συνδέονται ισχυρά με τους τελεστέους.Παράδειγμα:
struct wordinfo *pw;
Με την έκφραση ++pw->nlines αυξάνει το nlines γιατί είναι ισοδύναμη με την ++(pw->nlines). Αν θέλαμε να αυξήσουμε τον δείκτη pw θα έπρεπε να γράψουμε (++pw)->nlines ή (pw++)->nlines2, ανάλογα με το πότε θα επιθυμούσαμε να γίνει η αύξηση του δείκτη, πριν ή μετά την προσπέλαση του μέλους nlines.
Παράδειγμα:
int main(void)
{ int i, n;
struct worddata {
char *word;
int numb;
char let;
} wordarray[] = {
{"ABCD", 1, 'a'}, {"EF", 2, 'b'},
{"GHIJ", 3, 'c'}, {"KL", 4, 'd'},
{"M", 5, 'e'}};
struct worddata *p = wordarray;
n = sizeof(wordarray)/sizeof(struct worddata);
printf("%d ", n);
for (i=0 ; i < 2 ; i++) {
printf("%c ", *p->word++);
printf("%s ", ++p->word);
printf("%c ", p->let++);
printf("%c ", (++p)->let);
printf("%d ", p++->numb);
printf("%d ", ++p->numb); }
for (i=0 ; i < n ; i++)
printf("%s %d %c ", wordarray[i].word,
wordarray[i].numb, wordarray[i].let);
printf("\n"); return 0; }
Τι θα εκτυπωθεί από το πρόγραμμα αυτό; 3
/* File: triangles.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
struct point {
double x; /* A point in 2 dimensions is defined */
double y; /* by its x- and y- coordinates */
} *points;
struct triangle {
struct point a; /* A triangle is defined by its three vertices */
struct point b;
struct point c;
};
struct triangle get_triangle(int, int, int);
double triangle_area(struct triangle *);
double get_side(struct point, struct point);
int main(int argc, char *argv[])
{ int i, j, k, n = 10;
long seed;
struct triangle tr, max_tr, min_tr;
double area, max_area, min_area;
seed = time(NULL);
if (argc > 1)
n = atoi(argv[1]);
if (n < 3) { /* In order to have at least one triangle */
printf("At least three points are needed\n");
return 1;
}
if ((points = malloc(n * sizeof(struct point))) == NULL) {
/* Allocate memory to store n points */
printf("Sorry, not enough memory!\n");
return 1;
}
/* Initialize random number generator */
srand((unsigned int) seed);
for (i=0 ; i < n ; i++) {
/* Generate points with coordinates between 0.0 and 100.0 */
/* RAND_MAX is max number that rand() returns - usually 2147483647 */
(points+i)->x = (100.0 * rand())/(RAND_MAX+1.0);
(points+i)->y = (100.0 * rand())/(RAND_MAX+1.0);
}
for (i=0 ; i < n ; i++) /* Printout points generated */
printf("P%-2d: (%4.1f,%4.1f)\n", i, (points+i)->x, (points+i)->y);
tr = get_triangle(0, 1, 2); /* Get first triangle */
area = triangle_area(&tr); /* Compute area of first triangle */
/* Initialize max and min triangles with first triangle */
max_tr = min_tr = tr;
max_area = min_area = area; /* Areas of max and min triangles */
for (i=0 ; i < n-2 ; i++) /* Iterate through all combinations */
for (j=i+1 ; j < n-1 ; j++) /* of points in order to form all */
for (k=j+1 ; k < n ; k++) { /* possible triangles */
tr = get_triangle(i, j, k); /* Get current triangle */
area = triangle_area(&tr); /* Area of current triangle */
if (area > max_area) { /* Is current larger than max? */
max_tr = tr;
max_area = area;
}
if (area < min_area) { /* Is current smaller than min? */
min_tr = tr;
min_area = area;
}
}
printf("\n");
printf("Max triangle: "); /* Printout max triangle */
printf("(%4.1f,%4.1f) ", max_tr.a.x, max_tr.a.y);
printf("(%4.1f,%4.1f) ", max_tr.b.x, max_tr.b.y);
printf("(%4.1f,%4.1f) ", max_tr.c.x, max_tr.c.y);
printf(" Area: %10.5f\n", max_area);
printf("Min triangle: "); /* Printout min triangle */
printf("(%4.1f,%4.1f) ", min_tr.a.x, min_tr.a.y);
printf("(%4.1f,%4.1f) ", min_tr.b.x, min_tr.b.y);
printf("(%4.1f,%4.1f) ", min_tr.c.x, min_tr.c.y);
printf(" Area: %10.5f\n", min_area);
return 0;
}
struct triangle get_triangle(int i, int j, int k)
{ struct triangle tr;
tr.a = *(points+i);
tr.b = *(points+j);
tr.c = *(points+k);
return tr; /* Return triangle with vertices i, j, k */
}
double triangle_area(struct triangle *tr)
{ double s1, s2, s3, t;
s1 = get_side(tr->a, tr->b); /* Get length of side ab */
s2 = get_side(tr->b, tr->c); /* Get length of side bc */
s3 = get_side(tr->c, tr->a); /* Get length of side ca */
t = (s1+s2+s3)/2; /* Compute half of the perimeter */
return sqrt(t*(t-s1)*(t-s2)*(t-s3)); /* Return area */
}
double get_side(struct point p1, struct point p2)
{ /* Return Euclidean distance between points p1 and p2 */
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
% gcc -o triangles triangles.c -lm
% ./triangles 17
P0 : (37.5,51.6)
P1 : (31.4, 0.8)
P2 : (10.3,32.8)
P3 : ( 4.5,38.4)
P4 : (27.4,96.3)
P5 : (28.4,43.0)
P6 : (69.8,55.7)
P7 : (13.3,71.4)
P8 : (17.8,76.2)
P9 : ( 3.9, 8.8)
P10: (29.6,69.6)
P11: (20.6,43.3)
P12: ( 9.1,76.1)
P13: (33.9,25.6)
P14: (34.1,66.4)
P15: (15.1,22.9)
P16: (67.5,49.7)
Max triangle: (27.4,96.3) (69.8,55.7) ( 3.9, 8.8) Area: 2331.69411
Min triangle: (28.4,43.0) (17.8,76.2) (33.9,25.6) Area: 0.09948
%
Έστω η δήλωση:
struct listnode {
int value;
struct listnode *next;
};
Αυτή η δομή ορίζει έναν κόμβο λίστας στον οποίο φυλάσσεται ένας ακέραιος και ένας δείκτης σε κόμβο λίστας.
NULL.
15, 22, 37 και 12.malloc) και, φυσικά, η αποδέσμευση (μέσω free), όταν δεν χρειαζόμαστε πλέον τον κόμβο.malloc. Αυτό σημαίνει ότι δεν μπορούμε να έχουμε άμεση πρόσβαση στο \(N\)-οστό στοιχείο μίας λίστας, όπως στους πίνακες, αλλά μόνο ακολουθώντας την αλυσίδα των στοιχείων από το πρώτο έως το \(N\)-οστό.89 μεταξύ των στοιχείων 37 και 12.22 από την προηγούμενη λίστα.Τα παιδιά ενός κόμβου αναφέρονται σαν το αριστερό και το δεξί παιδί του κόμβου. Για την αναπαράσταση ενός κόμβου χρησιμοποιούμε μία αυτο-αναφορική δομή, όπως η εξής:
struct tnode {
int value;
struct tnode *left;
struct tnode *right;
};
struct tnode, σε κάθε κόμβο του δέντρου φυλάσσεται ένας ακέραιος, αλλά θα μπορούσαμε σαν πληροφορία προς φύλαξη να έχουμε δεδομένα οποιουδήποτε τύπου, όπως και στις λίστες, και, φυσικά, και περισσότερα του ενός δεδομένα. Οι δείκτες left και right είναι οι διευθύνσεις των κόμβων που είναι αριστερό και δεξί παιδί, αντίστοιχα, του κόμβου.NULL.malloc) για να φυλαχθεί κάθε κόμβος, ενώ όταν ένας κόμβος δεν χρειάζεται πλέον, πρέπει η μνήμη που καταλαμβάνει να αποδεσμεύεται (μέσω free).typedef, να δώσουμε δικά μας ονόματα σε τύπους που χρησιμοποιούμε συχνά, και μετά να ορίζουμε μεταβλητές αυτών των τύπων με βάση το νέο όνομα.Παραδείγματα:
typedef int Length;
typedef char *String;
typedef struct listnode *Listptr;
struct listnode {
int value;
Listptr next;
};
typedef struct tnode *Treeptr;
typedef struct tnode {
int value;
Treeptr left;
Treeptr right;
} Treenode;
Length len, maxlen;
String name, line[10];
Listptr a_list;
Treenode a_tree_node;
Treeptr a_tree;
Ένας τρόπος για εξοικονόμηση μνήμης είναι οι ενώσεις, που ορίζονται όπως οι δομές (με τη λέξη-κλειδί union). Δεν δεσμεύεται χώρος για όλα τα μέλη της όταν ορίζεται μία μεταβλητή τύπου ένωσης, αλλά αυτός που απαιτείται για τη φύλαξη του μέλους με τις μεγαλύτερες απαιτήσεις σε μνήμη. Όλα τα μέλη φυλάσσονται σ’ αυτόν τον χώρο. Παράδειγμα:
union alternative_data {
int selection;
int ivalue;
float fvalue;
char *svalue;
} id_numb;
Παράδειγμα:
struct {
unsigned int mode : 2;
unsigned int bool : 1;
unsigned int octal : 3;
} flags;
Μπορείτε να ορίσετε δομές και για άλλες γεωμετρικές οντότητες στο επίπεδο, για παράδειγμα, ευθύγραμμα τμήματα, τρίγωνα, κύκλους, ή, ανεξαρτήτως προσανατολισμού, τετράγωνα, ρόμβους, ορθογώνια παραλληλόγραμμα, (πλάγια) παραλληλόγραμμα και τυχαία τετράπλευρα; Μπορείτε να ορίσετε και γεωμετρικές οντότητες στον τρισδιάστατο χώρο; ↩
Εδώ δεν είναι απαραίτητες οι παρενθέσεις, αφού δεν θα υπήρχε αμφιβολία για το τι θα σήμαινε το pw++->nlines. ↩
5 A CD a b 2 4 G IJ c d 4 6 CD 1 b EF 2 b IJ 4 d KL 4 d M 6 e ↩
Δεν είναι ενδιαφέρον να μπορούμε να έχουμε και λίστες από λίστες; ↩
Ποιο θα ήταν το δέντρο αν η εισαγωγή των λέξεων γινόταν από την τελευταία λέξη της πρότασης προς την πρώτη; ↩