Προγραμματισμός Υπολογιστών με C++ ( 2012-13 ) 20η διάλεξη Ίων Ανδρουτσόπουλος http://www.aueb.gr/users/ion/ 1
Τι θα ακούσετε σήμερα Υλοποίηση στοίβας σε C. 2
Αναπαράσταση στοίβας StackNode n1 value 10 top Stack s counter 3 StackNode n2 value 30 StackNode n3 value 20 0 3
Η δομή (struct) StackNode struct snode { int value; struct snode* ; ; «δομές»: Μοιάζουν με τις τάξεις της C++, αλλά δεν μπορούν να έχουν μεθόδους και όλα τα μέλη είναι δημόσια. Πρέπει να γράφουμε και το «struct» όποτε χρησιμοποιούμε αυτόν τον τύπο (π.χ. «struct snode n3;» ή «struct snode* ;»). typedef struct snode StackNode; ή και τα δύο μαζί: typedef struct snode { int value; struct snode* ; StackNode; Μας επιτρέπει να γράφουμε «StackNode» αντί για «struct snode». Δουλεύει και με άλλους τύπους (π.χ. «typedef int akeraios»). 4
Το αρχείο κεφαλίδας stack.h #ifndef _STACK_H_ #define _STACK_H_ #include <stdio.h> typedef struct snode { int value; struct snode* ; StackNode; typedef struct { StackNode* top; unsigned int counter; Stack; void stackinit(stack* stackptr); unsigned int stacklength(stack* stackptr); void push(stack* stackptr, int valuein); int pop(stack* stackptr); #endif Χωρίς όνομα μετά το struct. Δεν μας χρειάζεται σε αυτή την περίπτωση. Αντί για κατασκευαστή. Θα μεταβιβάζουμε το Stack που θέλουμε να αρχικοποιήσουμε (άρα να τροποποιήσουμε) κατά αναφορά με χρήση δείκτη (δεν υπάρχουν αναφορές). Είναι μεμονωμένες συναρτήσεις, όχι μέθοδοι τάξεως (δεν έχουμε τάξεις). 5
Οι stackinit και stacklength void stackinit(stack* stackptr) { Stack checknullpointer(stackptr, "stackinit"); stackptr->top = 0; top 0 stackptr->counter = 0; counter 0 stackptr Όταν δημιουργούμε μια νέα στοίβα, θα καλούμε την stackinit για να αρχικοποιήσουμε τα top, counter. Δεν έχουμε κατασκευαστές. Η checknullpointer ελέγχει μήπως ο stackptr δείχνει στο 0. Αν ναι, τερματίζει την εκτέλεση του προγράμματος. unsigned int stacklength(stack* stackptr) { checknullpointer(stackptr, "stacklength"); return stackptr->counter; 6
Η συνάρτηση pop int pop(stack* stackptr) { StackNode* newtop; int valuetoreturn; checknullpointer(stackptr, "pop"); if(stackptr->counter = = 0) { fprintf(stderr, "%s: pop called \ with empty stack.\n", programname); exit(1); newtop = stackptr->top->; valuetoreturn = stackptr->top->value; free(stackptr->top); stackptr->top = newtop; stackptr->counter--; top return valuetoreturn; stackptr newtop Stack s counter 3 StackNode n1 value 10 StackNode n2 value 30 StackNode n3 value 20 0 7
Η συνάρτηση push void push(stack* stackptr, int valuein) { StackNode* newtop; checknullpointer(stackptr, "push"); newtop = (StackNode*)malloc(sizeof(StackNode)); newtop->value = valuein; newtop-> = stackptr->top; stackptr->top = newtop; stackptr->counter++; stackptr top Stack s counter 3 StackNode n1 value 10 StackNode n2 value 30 StackNode n3 value 20 newtop 0 8
Περισσότερα για τη malloc Δεν υπάρχει new στη C. Η malloc απαιτεί ως όρισμα το μέγεθος του αιτούμενου χώρου μνήμης. Το sizeof(stacknode) υπολογίζει πόσο χώρο μνήμης απαιτεί μια δομή StackNode. H sizeof μπορεί να χρησιμοποιηθεί και με άλλους τύπους (π.χ. sizeof(int)). Υπάρχει και στη C++. Η malloc επιστρέφει δείκτη σε char. Μετατρέπουμε σε StackNode* το δείκτη που επιστρέφει. Διαφορετικά οι περισσότεροι μεταγλωττιστές τυπώνουν προειδοποίηση κατά την εκχώρηση του αποτελέσματος της malloc στο δείκτη newtop (που είναι τύπου StackNode*). 9
H checknullpointer καθολική μεταβλητή void*: Για οποιονδήποτε τύπο δείκτη... void checknullpointer(void* pointer, char* functionname) { if(pointer = = 0) { fprintf(stderr, "%s: null pointer passed to function %s.\n", programname, functionname); exit(1); 10
Το αρχείο stack.c #include "stack.h" Χρήση καθολικής μεταβλητής που έχει δηλωθεί σε άλλο αρχείο.c. extern char programname[]; void checknullpointer(void* pointer, char* functionname) { if(pointer = = 0) { fprintf(stderr, "%s: null pointer passed to function %s.\n", programname, functionname); exit(1); void stackinit(stack* stackptr) { /* Όπως πριν... Ακολούθως οι ορισμοί των υπολοίπων συναρτήσεων. */ 11
Το αρχείο main.c #include <stdio.h> #include <string.h> #include "stack.h" #define PROGRAM_NAME_LENGTH 10 char programname[program_name_length]; int main(int argc, char **argv) { FILE* inputfile; Stack mystack; int inputvalue; strncpy(programname, argv[0], PROGRAM_NAME_LENGTH - 1); if(argc!= 2) { fprintf(stderr, "%s: bad number of arguments.\n", programname); exit(1); if((inputfile = fopen(argv[1], "r")) = = NULL) { fprintf(stderr, "%s: could not open input file.\n", programname); exit(1); 12
Το αρχείο main.c συνέχεια... stackinit(&mystack); while(fscanf(inputfile, "%d", &inputvalue)!= EOF) { printf("value read: %d\n", inputvalue); push(&mystack, inputvalue); fclose(inputfile); printf("number of values pushed: %d\n", stacklength(&mystack)); while(stacklength(&mystack)!= 0) { printf("popped value: %d\n", pop(&mystack)); 13