rafail1994 Δημοσ. 13 Μαΐου 2013 Μέλος Δημοσ. 13 Μαΐου 2013 Ευχαριστώ πάρα πολύ για της επεξηγήσεις .Εγώ πρέπει να φτιάξω δυο πίνακες αυτής της μορφής (όπως φαίνονται παρακάνω) μπορώ να τους δημιουργήσω από την αρχή έτσι ; , η με κάποιον τρόπο (realloc ; ) να διαγράψω τα "κενά" μετά ;
imitheos Δημοσ. 13 Μαΐου 2013 Δημοσ. 13 Μαΐου 2013 Να σβήσεις κενά όχι αλλά μπορείς να τον ορίσεις σαν 5x3 και να αγνοήσεις τα 3 στοιχεία που δεν χρειάζεσαι. Αν ορίσεις δυναμικά τον πίνακα με διπλό δείκτη (την 2η μέθοδο που περιέγραψα και την 1η που περιέγραψε ο migf1), τότε μπορείς να έχεις ανομοιογενείς "γραμμές" και να τον κάνεις ακριβώς όπως τον δείχνεις.
rafail1994 Δημοσ. 13 Μαΐου 2013 Μέλος Δημοσ. 13 Μαΐου 2013 Ωραία ευχαριστώ και πάλι θα το δοκιμάσω και θα πω αποτελέσματα !!
migf1 Δημοσ. 14 Μαΐου 2013 Δημοσ. 14 Μαΐου 2013 Δεν είμαι καθόλου σίγουρος αν είχαν κάτι τόσο δυναμικό στο μυαλό τους στην σχολή σου με αυτή την εργασία, αλλά όπως και να ΄χει παραθέτω μια εκδοχή που σκάρωσα στα γρήγορα (καμιά ώρα δηλαδή)... το κυρίως ζουμί είναι στην arr_new()... #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #define MAXINPUT (255+1) #define STOP_INPUT "stop" /* ----------------------------------------------- * Get a string from stdin, drop the '\n' and return its length. */ int s_get( char *s, int size ) { int i, c; if ( !s ) return -1; for (i=0; i < size-1 && EOF != (c = getchar()) && c != '\n'; i++ ) *s++ = c; *s = '\0'; return i; } /* ----------------------------------------------- * Set the first nrows of arr to NULL. */ void arr_invalidate_rows( int **arr, int nrows ) { int i; if ( !arr ) return; for (i=0; i < nrows; i++) arr[i] = NULL; } /* ----------------------------------------------- * Print the contents of arr. * Rows are expected to be NULL terminated, * while cols are expected to be INT_MAX terminated. */ void arr_print( int **arr, const char *title ) { int i,j; if ( title ) { printf( "%s", title ); fflush( stdout ); } if ( !arr ) { puts( "<NULL>" ); return; } for (i=0; NULL != arr[i]; i++) { for (j=0; INT_MAX != arr[i][j]; j++) { printf( "%d ", arr[i][j] ); fflush(stdout); } putchar( '\n' ); } putchar( '\n' ); } /* ----------------------------------------------- * Free memory reserved for arr. * Rows are expected to be NULL terminated. */ void arr_free( int ***arr ) { int i; if ( !arr ) return; for (i=0; (*arr)[i]; i++) free( (*arr)[i] ); free( *arr ); } /* ----------------------------------------------- * Return a newly created arr, according to user input. * nrows passes to the caller the total number of rows * created, excluding the NULL terminating one. */ int **arr_new( int *nrows, const char *prompt ) { const int AHEAD = 16; /* alloc ahead buffer, in elements */ int **ret = NULL, **try = NULL; int *tryCols = NULL; int i=0, j=0; char *delims = "\t\n "; int ncols = 0; char input[MAXINPUT] = {'\0'}; if ( !nrows ) goto fail; if ( prompt ) { printf( "%s", prompt ); fflush( stdout ); } /* create alloc-ahead buffer for rows */ *nrows = AHEAD; ret = malloc( *nrows * sizeof(int *) ); if ( NULL == ret ) goto fail; arr_invalidate_rows( ret, *nrows ); /* make them all NULL */ /* * read input lines (rows) from stdin * tokenize each line to integers (cols) * and store each integer into each curent row as a new col */ for (i=0; s_get(input, MAXINPUT) > 0; i++ ) { char *cp = NULL; /* string token */ /* stop when user types the STOP_INPUT string */ if ( 0 == strcmp(input, STOP_INPUT) ) break; /* need more mem for rows? */ if ( i == *nrows-1 ) { /* double their allocated size */ try = realloc(ret, 2 * (*nrows) * sizeof(int *) ); if ( NULL == try ) goto fail; arr_invalidate_rows( &try[i], *nrows ); ret = try; (*nrows) += (*nrows); /* remember new count of rows */ } /* create alloc-ahead buffer for cols in current row */ ncols = AHEAD; ret[i] = calloc(ncols, sizeof(int) ); if ( NULL == ret[i] ) goto fail; /* tokenize input line into integers & store them as cols at current row */ j = 0; for (cp = strtok(input,delims); cp; cp = strtok(NULL,delims) ) { /* need more mem for cols in current row ? */ if ( j == ncols-1 ) { /* double their allocated size */ tryCols = realloc(ret[i], 2 * ncols * sizeof(int) ); if ( NULL == try ) goto fail; ret[i] = tryCols; ncols += ncols; /* remember new count of cols */ } /* convert string token to integer & store it */ if ( 1 != sscanf(cp, "%d", &ret[i][j]) ) goto fail; /* INT_MAX is allowed, but no further inputting for this row */ if ( INT_MAX == ret[i][j] ) break; j++; /* current col */ } ret[i][j++] = INT_MAX; /* add terminating value */ /* free any extra cols from memory for this row */ tryCols = realloc(ret[i], j * sizeof(int) ); if ( NULL == tryCols ) goto fail; ret[i] = tryCols; ncols = j-1; /* exclude terminating value */ } ret[i++] = NULL; /* NULL termninate rows */ /* free any extra rows from memory */ try = realloc( ret, i * sizeof(int *) ); if ( NULL == try ) goto fail; ret = try; *nrows = i-1; /* exclude NULL terminating row */ return ret; fail: puts( "*** failure to make a new arr... " ); if ( ret ) arr_free( &ret ); return NULL; } /* ----------------------------------------------- * Entry point of the program */ int main( void ) { int **arr1 = NULL, **arr2 = NULL; int nrows1 = 0, nrows2 = 0; arr1 = arr_new( &nrows1, "ENTER CONTENTS OF ARR1:\n" ); if ( NULL == arr1 ) goto fail; arr2 = arr_new( &nrows2, "ENTER CONTENTS OF ARR2:\n" ); if ( NULL == arr2 ) goto fail; arr_print( arr1, "ARR1 CONTENTS:\n" ); arr_print( arr2, "ARR2 CONTENTS:\n" ); arr_free( &arr1 ); arr_free( &arr2 ); system( "pause" ); exit(0); fail: puts( "aborting program... " ); if ( arr1 ) arr_free( &arr1 ); if ( arr2 ) arr_free( &arr2 ); system( "pause" ); exit(1); } Έξοδος: ENTER CONTENTS OF ARR1: 1 3 4 0 2 1 3 0 2 4 0 3 stop ENTER CONTENTS OF ARR2: 5 5 7 5 7 7 6 5 6 6 7 6 stop ARR1 CONTENTS: 1 3 4 0 2 1 3 0 2 4 0 3 ARR2 CONTENTS: 5 5 7 5 7 7 6 5 6 6 7 6 Press any key to continue . . . Την s_get() δεν την έχω κάνει δυναμική (βαρέθηκα) αλλά αν είσαι σε Unix/Linux/OSX μπορείς να χρησιμοποιήσεις την getline(). Δεν ξέρω αν και πόση άκρη θα βγάλεις, αλλά τα rows (γραμμές) τα έχω NULL terminated ενώ τα columns τα έχω INT_MAX terminated. Αυτό βοηθάει στο να μη χρειάζεται να κουβαλάμε μαζί μας τα nrows και τα ncols σε όλες τις συναρτήσεις διαχείρισης (π.χ. arr_free(), arr_print(), κλπ) αλλά αν τα έχουμε κι αυτά δεν χρειάζεται να τα υπολογίζουμε κάθε φορά. Αν και την arr_new() την έχω κάνει να σου επιστρέφει και το nrows αν θελήσεις να το χρησιμοποιήσεις... απλώς σημείωσε πως δεν περιλαμβάνει το terminating NULL. Το ncols δεν το επιστρέφει πουθενά η arr_new() γιατί κάτι τέτοιο θέλει ειδική μεταχείριση (π.χ. να το αποθήκευα στην αρχή των columns σε κάθε γραμμή) αλλά είναι περασμένη τώρα η ώρα για τέτοια. Οπότε τα έβαλα INT_MAX terminated που είναι πιο εύκολο. Την μνήμη την κάνω pre-allocate σε 16 στοιχεία, τόσο για τις γραμμές όσο και για τις στήλες, κι όταν εξαντληθούν διπλασιάζουν το μέγεθός του. Επίσης, όταν τελειώσει το input (πρέπει να γράψει "stop" ο χρήστης σε ξεχωριστή γραμμή ή να πατήσει ENTER χωρίς να γράψει κάτι) τυχόν έξτρα pre-allocated μνήμη απελευθερώνεται, άλλα το μήκος των γραμμών και των στηλών είναι ακριβώς όσα μας πληκτρολόγησε ο χρήστης, +1 για τους terminators (NULL στις γραμμές, INT_MAX στις στήλες) Ελπίζω να βγάλεις λίγη άκρη, αν όχι ρώτα 1
rafail1994 Δημοσ. 16 Μαΐου 2013 Μέλος Δημοσ. 16 Μαΐου 2013 Όντως δεν έβγαλα και πολύ άκρη τα έχασα μέσα σε τόσες γραμμές κώδικα Για να σας δώσω να καταλάβετε τι ακριβός θέλει δείτε την εκφώνηση της άσκησης Σε ένα γράφημα, του οποίου οι κορυφές συνδέονται με ακμές οι οποίες έχουν δοθέντα βάρη, ζητείται να βρεθεί ένα μονοπάτι το οποίο να περνά μια μόνο φορά από κάθε κορυφή και να έχει το μικρότερο κόστος (Πρόβλημα του περιοδεύοντας εμπόρου). Ως κόστος για το μονοπάτι ορίζεται το άθροισμα των βαρών των ακμών από τις οποίες διέρχεται.. Ένας αλγόριθμος που θα έδινε μια καλή λύση (όχι τη βέλτιστη) ξεκινά από μια κορυφή εκκίνησης και ως επόμενη κορυφή για το μονοπάτι ορίζει αυτή με την οποία η κορυφή εκκίνησης συνδέεται με την ακμή που έχει το μικρότερο βάρος. Στη συνέχεια η κορυφή εκκίνησης αφαιρείται από το γράφημα και η διαδικασία επαναλαμβάνεται θεωρώντας ως κορυφή εκκίνησης την επόμενη κορυφή που εντοπίστηκε με το πιο πάνω κριτήριο. Ο αλγόριθμος τερματίζεται όταν το μονοπάτι περάσει από όλες τις κορυφές ή όταν διαπιστώσει ότι δεν υπάρχουν κορυφές οι οποίες να συνδέονται με την κορυφή που στο συγκεκριμένο στάδιο εκτέλεσης του αλγόριθμου θεωρείται ως κορυφή εκκίνησης. Να γραφεί το πρόγραμμα που να υλοποιεί τον πιο πάνω αλγόριθμο. Για την εισαγωγή ενός γραφήματος, που αποτελείται από Ν κορυφές, να αριθμούνται οι κορυφές από το 0 έως το Ν-1. Στη συνέχεια να ορίζεται o πίνακας connection, δύο διαστάσεων, σε κάθε γραμμή του οποίου να αντιστοιχεί και μια κορυφή του γραφήματος. Κάθε γραμμή του πίνακα να έχει τόσες θέσεις όσες είναι και οι κορυφές του γραφήματος που συνδέονται με την κορυφή στην οποία αντιστοιχεί η γραμμή (βαθμός της κορυφής). Στις θέσεις αυτές να καταχωρούνται οι αύξοντες αριθμοί των κορυφών που συνδέονται με την κορυφή που αντιστοιχεί στη γραμμή. Για την καταχώρηση των βαρών των ακμών που συνδέουν τις κορυφές να ορίζεται o πίνακας weights, όμοιος με τον πίνακα connection, στον οποίο, σε κάθε γραμμή, να αντιστοιχεί επίσης μια κορυφή. Στον πίνακα αυτόν να έχουν αντικατασταθεί οι αύξοντες αριθμοί των κορυφών που αναγράφονται στον πίνακα connection με τα βάρη των ακμών που τις συνδέουν με την κορυφή στην οποία αντιστοιχεί η γραμμή στην οποία βρίσκονται. Στο πρόγραμμα να ορίζεται η συνάρτηση void next_vertex(…) η οποία να δέχεται τον αύξοντα αριθμό της κορυφής εκκίνησης και να βρίσκει την επόμενη κορυφή του μονοπατιού σύμφωνα με τον πιο πάνω αλγόριθμο. Στη συνέχεια, μέσα από μια recursion διαδικασία, να καλεί τον εαυτό της δίνοντας ως κορυφή εκκίνησης την κορυφή που εντόπισε ως επόμενη κορυφή για το μονοπάτι. Το πρόγραμμα αφού διαβάσει τον αριθμό Ν των κορυφών του γραφήματος και τον βαθμό της κάθε κορυφής, να δεσμεύει δυναμικά την ελάχιστη απαραίτητη μνήμη για την καταχώρηση των πινάκων που ορίζουν το γράφημα και να διαβάζει και να καταχωρεί τις αντίστοιχες τιμές στους πίνακες. Στη συνέχεια να διαβάζει τον αύξοντα αριθμό για την κορυφή εκκίνησης και αφού καλέσει τη συνάρτηση next_vertex(), να τυπώνει το μονοπάτι που βρέθηκε καθώς και το αντίστοιχο κόστος. Στην περίπτωση που ο αλγόριθμος δε μπόρεσε να περάσει το μονοπάτι από όλες τις κορυφές του γραφήματος να τυπώνεται αντίστοιχο μήνυμα.
temp_ Δημοσ. 17 Μαΐου 2013 Δημοσ. 17 Μαΐου 2013 (επεξεργασμένο) Πρόκειται για το Traveling Salesman Problem (TSP) μοντελοποιημένο με γράφο. Η εκφώνηση σου δίνει έναν μη-βέλτιστο αλγόριθμο για να βρεις το shortest-path μεταξύ 2 τυχαίων κορυφών. Σε ότι αφορά τον κώδικα του προηγούμενου μηνύματος, η βασική συνάρτηση που χρειάζεται να κατανοήσεις για να δημιουργήσεις τους 2Δ πίνακες connections και weights είναι η arr_new(). Η συγκεκριμένη είναι λίγο πολύπλοκη γιατί εκτός από την δυναμική δέσμευση διαβάζει κιόλας την κύρια είσοδο, την σπάει σε integer tokens και τα βάζει στον πίνακα ως στήλες. Συν τοις άλλοις, χρησιμοποιεί και buffer προδέσμευσης μνήμης τόσο για τις γραμμές όσο και για τις στήλες, για να μειώσει το πλήθος των κλήσεων στις malloc() και calloc() επειδή είναι βαριές συναρτήσεις. Μια βασική δυσκολία που αντιμετωπίζουμε γενικώς όταν φτιάχνουμε έναν 2Δ πίνακα τελείως δυναμικά, είναι το που θα κρατήσουμε τα μήκη των γραμμών, αφού και η κάθε μια τους έχει και διαφορετικό. Τα μήκη τους τα χρειαζόμαστε διότι χωρίς αυτά δεν θα μπορούσαμε ας πούμε να τις τυπώσουμε στην οθόνη σε μεταγενέστερη στιγμή, αφού δεν θα ξέρουμε πόσα στοιχεία πρέπει να λουπάρουμε (πόσες στήλες έχει δηλαδή η κάθε γραμμή). Μια λύση που χρησιμοποιεί ο προηγούμενος κώδικας είναι να προσθέτει στο τέλος της κάθε γραμμής μια έξτρα στήλη με τιμή INT_MAX (<limits.h>). Αυτή την τιμή την δεσμεύει για δικιά του χρήση το πρόγραμμα αυτό (την απορρίπτει αν του τη δώσει ο χρήστης). Όταν λοιπόν θέλει να τυπώσει οποιαδήποτε γραμμή του 2Δ πίνακα, πιάνει τα στοιχεία της γραμμής που το ενδιαφέρει και τα τυπώνει μέχρι να βρει το στοιχείο με τιμή INT_MAX ... προσομοιώνει δηλαδή το '\0' που έχουμε στα strings. Ο κώδικας εκείνος κάνεις το ίδιο πράγμα και για το πλήθος το γραμμών, μόνο που το τερματίζει με NULL μιας και οι γραμμές είναι δείκτες σε int. Ταυτόχρονα όμως παρέχει και την μεταβλητή nrows ως εναλλακτική - χωρίς να μετράει το NULL - μιας και είναι εύκολο να το κάνει (πρόκειται για μια μόνο μεταβλητή, ενώ για τις στήλες θα ήθελε τόσες μεταβλητές όσες και γραμμές). Μια άλλη λύση για την αποθήκευση του μήκους των γραμμών είναι σε κάθε γραμμή να χρησιμοποιούμε την 1η της στήλη (ή τις 2 πρώτες της, βασικά τα bytes μας ενδιαφέρουν, ανάλογα με το πόσο αναμένουμε να είναι το μέγιστο πλήθος στοιχείων της μεγαλύτερης γραμμής μας). Εκεί λοιπόν αποθηκεύουμε το πλήθος στοιχείων της γραμμής, οπότε όταν το θελήσουμε αργότερα το βρίσκουμε αμέσως κοιτάζοντας την 1η της στήλη (προφανώς αυτή η στήλη παύει ποια να λογίζεται ως έγκυρο στοιχείο της γραμμής). Αυτή την τεχνική χρησιμοποιούσε π.χ. η Pascal και η Delphi αργότερα στα strings της, τα οποία σε αντίθεση με την C δεν χρειάζεται να τελειώνουν σε '\0' συν ότι μπορούν να περιέχουν το '\0' οπουδήποτε μέσα τους (κάτι πολύ χρήσιμο π.χ. όταν μεταφέρουμε δεδομένα στο δίκτυο, που τα θέλουμε να είναι "binary safe" όπως λέγεται... δηλαδή να μπορούν περιέχουν 1 ή περισσότερα μηδενικά bytes μέσα τους). Έχουν άλλα προβλήματα όμως.. τέσπα ξέφυγα πολύ Ευτυχώς όμως για σένα, η άσκησή σου από ότι κατάλαβα από την εκφώνηση της, σου λέει να πάρεις έτοιμα στην είσοδο όχι μόνο το Ν ως πλήθος των κορυφών, αλλά και τον βαθμό της κάθε κορυφής, ας τον πούμε β. Αυτό στην πράξη σημαίνει πως παίρνεις έτοιμα τα μήκη της κάθε γραμμής, αφού για κάθε κορυφή 0 έως N-1 έχει αντίστοιχους βαθμούς β0 έως β(N-1). Μπορείς λοιπόν να φτιάξεις έναν δυναμικό πίνακα Ν ακεραίων με το κάθε στοιχείο του να περιέχει το αντίστοιχο β, όπως θα σου δοθούν έτοιμα. Κατόπιν είναι εύκολο να φτιάξεις δυναμικά και τους connections και weights... // create 1D for input and read data into it int *input = calloc(N, sizeof(int) ); if ( NULL == input ) return 1; for (int i=0; i < N; i++) { scanf( "%d", &input[i] ); } // create the connections 2D array int **connections = calloc(N, sizeof(int *) ); if ( NULL == connections ) { free( input ); return 1; } for (int i=0; i < N; i++) { connections[i] = calloc( input[i], sizeof(int) ); if ( NULL == connections[i] ) { for (int j=i-1; j > -1; j--) free( connections[j] ); free( connections ); free( input ); return 1; } } // create the weights 2D array int **weights = calloc(N, sizeof(int *) ); if ( NULL == weights ) { free2d( connections, N ); free( input ); return 1; } for (int i=0; i < N; i++) { weights[i] = calloc( input[i], sizeof(int) ); if ( NULL == weights[i] ) { for (int j=i-1; j > -1; j--) free( weights[j] ); free( weights ); free2d( connections, N ); free( input ); return 1; } } ... Σε αυτό το σημείο για κάθε -1 < i < N ξέρεις ανά πάσα στιγμή πως το μήκος της γραμμής i τόσο του connections όσο και του weights είναι input[ i ]. Επιπρόσθετα, τους connections & weights τους έχεις ήδη δημιουργημένους, περιμένοντας να γεμίσουν από τον αλγόριθμο που θα χρησιμοποιήσεις για να περπατήσεις τον γράφο σου. Όταν τελειώσεις, μην ξεχάσεις να απελευθερώσεις τη μνήμη τόσο για τον input (με free(input) ) όσο και για τους connections & weights (me free2d(conenctions, N) και free2d(weights, N) ). Την free2d() θα πρέπει να την γράψεις εσύ. Θα είναι παραπλήσια με την arr_free() του προηγούμενου κώδικα, μόνο που εκείνη δεν παίρνει ως όρισμα το πλήθος των γραμμών γιατί εκεί έχει βάλει το NULL ως τελευταία γραμμή, οπότε μόλις το βρει σταματάει. Αν δεν ήταν τόσο συγκεκριμένη η εκφώνηση στην υλοποίηση των πινάκων, θα μπορούσες να απεμπλακείς τελείως από τους 2Δ πίνακες και να χρησιμοποιούσες μονάχα 1Δ σε συνδυασμό με κατάλληλες δομές (struct). Για παράδειγμα, οι παρακάτω δυο δομές περιγράφουν μια οποιαδήποτε κορυφή (Vertex) κι ένα οποιοδήποτε γράφο (Graph) συμπυκνώνοντας μέσα τους όλες τις απαραίτητες πληροφορίες που ορίζει η εκφώνηση της εργασίας, χωρίς να χρησιμοποιούν καθόλου 2Δ πίνακες... typedef struct Vertex { int id; int degree; int *connections; /* will be allocated dynamically to 'degree' integers */ int *weights; /* will be allocated dynamically to 'degree' integers */ }Vertex; typedef struct Graph { int nvert; /* number of vertices in graph */ Vertex *vert; /* dynamic array of nvert vertices */ }Graph; Οι πίνακες connections & weights περιέχονται πλέον μέσα στη δομή της κάθε κορυφής ως δυναμικοί 1Δ πίνακες, οι οποίοι θα δεσμεύσουν μνήμη όταν θα ξέρουμε την τιμή του βαθμού (degree) για την εκάστοτε κορυφή. Η δομή του γράφου περιέχει το πλήθος των κορυφών (nvert, δηλαδή το N) ώστε όταν γνωστοποιηθεί να μπορέσουμε να δεσμεύσουμε ανάλογη μνήμη για τον 1Δ δυναμικό πίνακα vert, ο οποίος επίσης περιέχεται στην δομή του γράφου. Αυτός ο πίνακας vert είναι ο αντίστοιχος του input στο προηγούμενο παράδειγμα, μόνο που τώρα έχουμε συγκεντρωμένα σε μια δομή και τον πίνακα και το πλήθος του. Χρησιμοποιώντας 1Δ πίνακες αντί για 2Δ απλοποιούμε σημαντικά την απαιτούμενη διαχείριση μνήμης (δέσμευση, αποδέσμευση, κλπ). Επιπλέον, χρησιμοποιώντας δομές μπορούμε να οργανώσουμε τον κώδικά μας πολύ καλύτερα, καθώς και να τον κάνουμε πιο δεκτικό σε μεταγενέστερες αλλαγές (refactoring, scaling, κλπ). Το βασικό αντίτιμο είναι η πιο σύνθετη σύνταξη και η πιο nested λογική, η οποία είναι πιο χρονοβόρα ως αρχικός σχεδιασμός. Ο κώδικας που ακολουθεί χρησιμοποιεί τις παραπάνω δομές για να δημιουργήσει ένα γράφο με Ν κορυφές μέσω της συνάρτησης new_graph(N); Το id της κάθε κορυφής αριθμείται αυτόματα από 0 έως Ν-1. Κατόπιν, με την συνάρτηση graph_set_vertices_randomly( graph, MAX_DEGREE, MAX_WEIGHT ); γεμίζει με τυχαίες τιμές τα υπόλοιπα πεδία της κάθε κορυφής (πλην δηλαδή των 'id' τους, αφού είναι ήδη αριθμημένα). Κατά την διαδικασία του γεμίσματος των κορυφών με τυχαίες τιμές δημιουργούνται και οι πίνακες connections & weights στην κάθε κορυφή, με τόσα στοιχεία όσα υπαγορεύει η τυχαία τιμή που ανατίθεται στο πεδίο degree της κάθε κορυφής. Οι τιμές των στοιχείων των πινάκων μπαίνουν κι αυτές τυχαία, με άνω όριο τα ορίσματα maxDegree και maxWeight της συνάρτησης. Τέλος τα περιεχόμενα του γράφου τυπώνονται με την graph_print(graph, title), και αποδεσμεύεται η μνήμη που καταλαμβάνει μέσω της graph_free( &graph ). #include <stdio.h> #include <stdlib.h> #include <time.h> #define ERRMSG( msg ) \ fprintf( stderr, "*** %s|%s():%d %s\n", __FILE__, __func__, __LINE__, (msg) ) typedef struct Vertex { int id; int degree; int *connections; /* will be allocated dynamically to 'degree' integers */ int *weights; /* will be allocated dynamically to 'degree' integers */ } Vertex; typedef struct Graph { int nvert; /* number of vertices in graph */ Vertex *vert; /* dynamic array of nvert vertices */ }Graph; /* ----------------------------------------------- * Create and return a new graph with 'n' vertices. Each vertex in the * 'graph->vert' array gets its 'id' field initialized to a sequentially * increased integer, from 0 to n-1. */ Graph *new_graph( int n ) { int i; Graph *g = NULL; /* graph to be created & returned */ if ( n < 1 ) goto fail_inval; /* alloc mem for a graph object */ g = calloc( 1, sizeof(Graph) ); if ( NULL == g ) goto fail_nomem; /* alloc mem for 'n' vertices and return it to g->vert */ g->vert = calloc( n, sizeof(Vertex) ); if ( NULL == g->vert ) { free( g ); goto fail_nomem; } /* set the 'id's of the allocated vertices */ for (i=0; i < n; i++ ) { g->vert[i].id = i; g->vert[i].degree = -1; g->vert[i].connections = NULL; g->vert[i].weights = NULL; } g->nvert = n; return g; fail_inval: ERRMSG( "Invalid function argument!" ); return NULL; fail_nomem: ERRMSG( "Possible memory shortage!" ); return NULL; } /* ----------------------------------------------- * Free the memory reserved for the specified graph. */ void graph_free( Graph **graph ) /* pass by reference */ { int i; Graph *g = NULL; /* just for brevity later on */ if ( !graph || !*graph ) return; g = *graph; if ( g->vert ) { for (i=0; i < g->nvert; i++) { if ( g->vert[i].connections ) free( g->vert[i].connections ); if ( g->vert[i].weights ) free( g->vert[i].weights ); } free( g->vert ); } free( *graph ); *graph = NULL; return; } /* ----------------------------------------------- * Print the contents of the specified graph. * The 'title' cstring is not printed if passed as NULL. */ void graph_print( const Graph *graph, const char *title ) { int i,j; if ( !graph ) { ERRMSG( "Invalid function argument!" ); return; } if ( title ) puts( title ); printf( "nvert: %d\n", graph->nvert ); printf( "vert @0x%x:\n", (unsigned long)graph->vert ); putchar( '\n' ); if ( !graph->vert ) return; for (i=0; i < graph->nvert; i++) { printf( "\tid : %d\n", graph->vert[i].id ); printf( "\tdegree: %d\n", graph->vert[i].degree ); printf( "\tconnections: " ); /* print connections, if any */ if ( NULL == graph->vert[i].connections ) { puts( "<does not exist>" ); } else { for (j=0; j < graph->vert[i].degree; j++) { printf( "%3d ", graph->vert[i].connections[j] ); fflush( stdout ); } putchar( '\n' ); } printf( "\tweights : " ); /* print weights, if any */ if ( NULL == graph->vert[i].weights ) { puts( "<does not exist>" ); } else { for (j=0; j < graph->vert[i].degree; j++) { printf( "%3d ", graph->vert[i].weights[j] ); fflush( stdout ); } putchar( '\n' ); } putchar( '\n' ); } return; } /* ----------------------------------------------- * Set random values to the vertices of an already created graph. * * The 'id' field of each vertex is already auto-assigned during the creation * of the graph, so the function sets random values to all other fields of the * vertices. 'maxDegree' specifies the maximum value for the 'degree' field, * while 'maxWeight' specifies the maximum value for integers in the 'weights' * array inside each vertex. Both boundaries are inclusive, starting from 0. * * The function also allocates memory for 'graph->nvert' elements for the arrays * 'connections' & 'weights' inside each vertex. The former is populated with * integers selected randomly from the range [0, 'grahp->nvert') while for the * latter the range [0, 'maxWeight'] is used instead... notice that the upper * bound of the 1st range is exclusive, but the upper bound of the 2nd range is * inclusive. */ void graph_set_vertices_randomly( Graph *graph, int maxDegree, int maxWeight ) { int i,j, degree; if ( !graph || maxDegree < 1 ) { ERRMSG( "Invalid function argument!" ); return; } if ( !graph->vert || graph->nvert < 1 ) { ERRMSG( "graph->vert == NULL or graph->nvert < 1" ); return; } /* for each vertex in graph */ for (i=0; i < graph->nvert; i++) { Vertex *v = &graph->vert[i]; /* just for brevity */ degree = rand() % (maxDegree + 1); /* random degree from [0, maxDegree] */ if ( 0 != degree ) { /* create connections-array with degree elements & assign them random values from range [0, graph->nvert) */ v->connections = malloc( degree * sizeof(int) ); if ( NULL == v->connections ) { ERRMSG( "Failed to allocate connections-array!" ); return; } for (j=0; j < degree; j++) v->connections[j] = rand() % graph->nvert; /* create weights-array with degree elements & assign them random values from range [0, maxWeight] */ v->weights = malloc( degree * sizeof(int) ); if ( NULL == v->weights ) { ERRMSG( "Failed to allocate weights-array!" ); return; } for (j=0; j < degree; j++) v->weights[j] = rand() % (maxWeight+1); } v->degree = degree; } } /* ----------------------------------------------- * Entry point of the program */ int main( void ) { const int N = 4; /* number of vertices in graph */ const int MAX_DEGREE = 3; /* for random assignments in range [0, MAX_DEGREE] */ const int MAX_WEIGHT = 100; /* for random assignments in range [0, MAX_WEIGHT] */ Graph *graph = NULL; srand( time(NULL) ); /* init pseudo-random generator */ graph = new_graph(N); /* create a new graph of N vertices */ if ( NULL == graph ) goto fail; graph_set_vertices_randomly( graph, MAX_DEGREE, MAX_WEIGHT ); graph_print( graph, "--- GRAPH CONTENTS ---\n" ); graph_free( &graph ); system( "pause" ); exit(0); fail: puts( "aborting program... " ); if ( graph ) graph_free( &graph ); system( "pause" ); exit(1); } Ενδεικτική Έξοδος: --- GRAPH CONTENTS --- nvert: 4 vert @0x3e2c98: id : 0 degree: 0 connections: <does not exist> weights : <does not exist> id : 1 degree: 2 connections: 0 2 weights : 98 62 id : 2 degree: 1 connections: 3 weights : 58 id : 3 degree: 2 connections: 0 0 weights : 33 64 Επεξ/σία 17 Μαΐου 2013 από temp_ 2
rafail1994 Δημοσ. 17 Μαΐου 2013 Μέλος Δημοσ. 17 Μαΐου 2013 (επεξεργασμένο) Temp Σε ευχαριστώ παρά πολύ για την ανάλυση σου. Λοιπόν μετά απο μια βλακεία που έκανα και έχασα τον κώδικα που ειχα γραψει στα γρήγορα τον ξανά έγραψα αλλα δεν μπορώ να καταλάβω γιατι μου βγάζει οτι δεν υπάρχει διαθέσιμη μνήμη 5 edit:Το βρηκα το προβλημα #include <stdio.h> #include <stdlib.h> /*---------------------------------------------------------Άσκηση Ε-------------------------------------------------------------------------------*/ int main() { system("chcp 1253"); //Μεταβλητές int n,i,j; //Κορυφες του γραφιματος int **connection,*path,*degree; //Connection:Πινακας κορυφων, Path:Πινακας διαδρομης, degree:Ποιες κορυφες συνδεoνται με ποιες float **weights; //Weights:Πινακας βαρων ακμων /*--------------------------Διαβασμα δεδομενων-----------------------------*/ printf("Dwse twn ari8mo twn korifwn tou grafimatos :"); scanf("%d",&n); if((degree=(int *)malloc(n*sizeof(int)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 1\n"); exit(1); } if((connection=(int **)malloc(n*sizeof(int *)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 2\n"); exit(2); } if((weights=(float **)malloc(n*sizeof(float *)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 3\n"); exit(3); } for(i=0;i<n;i++) { printf("Dwse ton ba8mo korifis %d = ",i); scanf("%d",°ree[i]); if((connection[i]=(int *)malloc(degree[i]*sizeof(int)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 4\n"); exit(4); } } if((weights[i]=(float *)malloc(degree[i]*sizeof(float)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 5\n"); exit(5); } for(i=0;i<n;i++) { printf("Δώστε τους αύξοντες αριθμούς των κορυφών που συνδέονται με την κορυφή %d = ? ",i); for(j=0;j<degree[i];j++) { scanf("%d",&connection[i][j]); } } for(i=0;i<n;i++) { printf("Δώστε τα βάρη των ακμών που συνδέουν την κορυφή %d = ? ",i); for(j=0;j<degree[i];j++) { scanf("%f",&weights[i][j]); } } /*------------------Απελευθερωση της μνημης του δεσμευτικε-----------------*/ free(connection); free(weights); free(degree); /*-------------------------------------------------------------------------*/ system("PAUSE"); return 0; } Επεξ/σία 17 Μαΐου 2013 από rafail1994
imitheos Δημοσ. 17 Μαΐου 2013 Δημοσ. 17 Μαΐου 2013 Λοιπόν μετά απο μια βλακεία που έκανα και έχασα τον κώδικα που ειχα γραψει στα γρήγορα τον ξανά έγραψα αλλα δεν μπορώ να καταλάβω γιατι μου βγάζει οτι δεν υπάρχει διαθέσιμη μνήμη 5 for(i=0;i<n;i++) { blah blah } if((weights[i]=(float *)malloc(degree[i]*sizeof(float)))==NULL) { printf("Δεν υπάρχει αρκετή διαθέσιμη μνήμη στη θέση 5\n"); exit(5); } Έχεις βγει από το if και το i έχει τιμή n οπότε το weights που τρέχεις το malloc δεν είναι σωστό. Ακόμη, μπορείς να ενσωματώσεις όλες αυτές τις μεταβλητές σε μια struct ώστε να είναι πιο ευανάγνωστο και να φαίνεται άμεσα το νόημα της κάθε μεταβλητής όπως έκανε ο temp_. Επίσης αν μπορείς απέφυγε τις κλήσεις της system.
temp_ Δημοσ. 17 Μαΐου 2013 Δημοσ. 17 Μαΐου 2013 (επεξεργασμένο) Temp Σε ευχαριστώ παρά πολύ για την ανάλυση σου. Λοιπόν μετά απο μια βλακεία που έκανα και έχασα τον κώδικα που ειχα γραψει στα γρήγορα τον ξανά έγραψα... Παρακαλώ! Θα μπορούσες να τον κάνεις πάντως αντιγραφή & επικόλληση από εδώ Εκτός από την σωστή παρατήρηση του imitheos, έχει και θέματα με την διαχείριση μνήμης ο κώδικάς που παρέθεσες. Δεν ακολούθησες δηλαδή το μοτίβο που σου είχα περιγράψει στο προηγούμενο μήνυμα (δηλαδή με συγγραφή συνάρτησης free2d() ). Παραθέτω κώδικα βασισμένο στον τελευταίο που έδωσες. Την errexit() αν την χάνεις, να εξηγήσω πως είναι variadic συνάρτηση η οποία λειτουργικά δουλεύει σαν την printf() με την επιπλέον λειτουργία πως μόλις τυπώσει ότι της έχεις πει τερματίζει το πρόγραμμα με κωδικό αποτυχίας προς το λειτουργικό (αν το πρόγραμμα έχει γίνει compile σε Windows, τότε ζητάει από τον χρήστη να πατήσει ένα πλήκτρο πριν τον τερματισμό... όπως έγραψε και ο imitheos όμως, είναι καλύτερα αντί για system("pause") να γράψεις μια δικιά σου συνάρτηση με παραπλήσια λειτουργικότητα σε στάνταρ C, ώστε να τρέχει ίδια σε όλα τα λειτουργικά). Κώδικας: #include <stdio.h> #include <stdlib.h> #include <stdarg.h> /* detect if we are on Windows OS */ #if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) const int g_OS_WINDOWS = 1; #else const int g_OS_WINDOWS = 0; #endif /* ----------------------- Άσκηση Ε --------------------- */ /* ------------------------------------------------------ */ void free_int2d( int **int2d, size_t nelems ) { size_t i = 0; if ( !int2d || nelems < 1 ) return; for (i=0; i < nelems; i++) { if ( NULL != int2d[i] ) free( int2d[i] ); } int2d = NULL; } /* ------------------------------------------------------ */ void free_float2d( float **float2d, size_t nelems ) { size_t i = 0; if ( !float2d || nelems < 1 ) return; for (i=0; i < nelems; i++) { if ( NULL != float2d[i] ) free( float2d[i] ); } float2d = NULL; } /* ------------------------------------------------------ */ void errexit( const char *msgf, ... ) { va_list vargs; if ( !msgf ) return; va_start(vargs, msgf); vprintf( msgf, vargs ); va_end(vargs); if ( g_OS_WINDOWS ) system( "pause" ); exit( EXIT_FAILURE ); } /* ------------------------------------------------------ */ void print_graph( int n, // number of vertices in graph int *degree, // 1d array of vertex degrees in grpah int **connection, // 2d array of vertex connections in graph float **weights // 2d array of vertex weights in graph ) { int i,j; if ( n < 1 || !degree || !connection || !weights ) { printf( "*** Μη έγκυρο όρισμα στην συναρτηση %s\n", __func__ ); return; } putchar( '\n' ); puts( "------- ΠΕΡΙΕΧΟΜΕΝΑ ΓΡΑΦΟΥ -------" ); for (i=0; i < n; i++) { putchar( '\n' ); printf( "Κορυφή #%d\n", i ); printf( "\tβαθμός κορυφής: %d\n", degree[i] ); printf( "\tσυνδεδεμένες κορυφές : " ); for (j=0; j < degree[i]; j++) printf( "%4d ", connection[i][j] ); putchar( '\n' ); printf( "\tβάρη συνδεδεμένων ακμών: " ); for (j=0; j < degree[i]; j++) printf( "%4.2f ", weights[i][j] ); putchar( '\n' ); } putchar( '\n' ); } /* ------------------------------------------------------ */ int main( void ) { system("chcp 1253"); // Μεταβλητές int n, i,j; // n: Κορυφες του γράφου int **connection; // Πινακας κορυφων int *path; // Πινακας διαδρομης int *degree; // Ποιες κορυφες συνδεoνται με ποιες float **weights; // Πινακας βαρων ακμων printf("Πλήθος κορυφών γράφου: "); fflush( stdout ); scanf( "%d", &n ); degree = malloc( n * sizeof(int) ); if ( NULL == degree ) { errexit( "*** αδυναμία δέσμευσης μνήμης για τον πίνακα degree" ); } connection = malloc( n * sizeof(int *) ); if ( NULL == connection ) { free( degree ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ errexit( "*** αδυναμία δέσμευσης μνήμης για τον πίνακα connection" ); } weights = malloc( n * sizeof(float *) ); if ( NULL == weights ) { free( connection ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ free( degree ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ errexit( "*** αδυναμία δέσμευσης μνήμης για τον πίνακα weights" ); } /* for n vertices... */ for (i=0; i < n; i++) { putchar( '\n' ); printf( "Κορυφή #%d\n", i ); /* read & store the degree of current vertex in 'degree[i]; */ printf( "\tβαθμός κορυφής: " ); fflush( stdout ); scanf("%d", °ree[i] ); /* alloc 'degree[i]' columns for the 'connection[i]' row */ connection[i] = malloc( degree[i] * sizeof(int) ); if ( NULL == connection[i] ) { free_int2d( connection, i ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ free( degree ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ errexit( "*** αδυναμία δέσμευσης %d στηλών στη γραμμή connection[%d]\n", degree[i], i ); } /* alloc 'degree[i]' columns for the 'weights[i]' row */ weights[i] = malloc( degree[i] * sizeof(float) ); if ( NULL == weights[i] ) { free_float2d( weights, i ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ free_int2d( connection, degree[i] ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ free( degree ); // ΑΥΤΟ ΤΟ ΕΙΧΕΣ ΞΕΧΑΣΕΙ errexit( "*** αδυναμία δέσμευσης %d στηλών στη γραμμή weights[%d]\n", degree[i], i ); } /* read & store the connections of the current vertex */ printf( "\n\tΑύξοντες αριθμοί των %d κορυφών που συνδέονται με την τρέχουσα\n", degree[i] ); printf( "\t(διαχωρίστε τους με κενά ή με αλλαγές γραμμών): " ); fflush( stdout ); for (j=0; j < degree[i]; j++) scanf( "%d", &connection[i][j] ); /* read & store the weights of the edges connected to the current vertex */ printf( "\n\tΒάρη των %d ακμών που συνδέονται στην τρέχουσα κορυφή\n", degree[i] ); printf( "\t(διαχωρίστε τα με κενά ή με αλλαγές γραμμών): " ); fflush( stdout ); for (j=0; j < degree[i]; j++) scanf( "%f", &weights[i][j] ); } print_graph( n, degree, connection, weights ); /* free the memory reserved by this program */ free_float2d( weights, n ); free_int2d( connection, n ); free( degree ); /*------------------Απελευθερωση της μνημης του δεσμευτικε----------------- free(connection); free(weights); free(degree); -------------------------------------------------------------------------*/ system("PAUSE"); return 0; } Ενδεικτική Είσοδος: Πλήθος κορυφών γράφου: 3 Κορυφή #0 βαθμός κορυφής: 2 Αύξοντες αριθμοί των 2 κορυφών που συνδέονται με την τρέχουσα (διαχωρίστε τους με κενά ή με αλλαγές γραμμών): 0 2 Βάρη των 2 ακμών που συνδέονται στην τρέχουσα κορυφή (διαχωρίστε τα με κενά ή με αλλαγές γραμμών): 0.98 1.1 Κορυφή #1 βαθμός κορυφής: 1 Αύξοντες αριθμοί των 1 κορυφών που συνδέονται με την τρέχουσα (διαχωρίστε τους με κενά ή με αλλαγές γραμμών): 2 Βάρη των 1 ακμών που συνδέονται στην τρέχουσα κορυφή (διαχωρίστε τα με κενά ή με αλλαγές γραμμών): 1.0 Κορυφή #2 βαθμός κορυφής: 2 Αύξοντες αριθμοί των 2 κορυφών που συνδέονται με την τρέχουσα (διαχωρίστε τους με κενά ή με αλλαγές γραμμών): 1 2 Βάρη των 2 ακμών που συνδέονται στην τρέχουσα κορυφή (διαχωρίστε τα με κενά ή με αλλαγές γραμμών): 0.95 1.03 Ενδεικτική Έξοδος: ------- ΠΕΡΙΕΧΟΜΕΝΑ ΓΡΑΦΟΥ ------- Κορυφή #0 βαθμός κορυφής: 2 συνδεδεμένες κορυφές : 0 2 βάρη συνδεδεμένων ακμών: 0.98 1.10 Κορυφή #1 βαθμός κορυφής: 1 συνδεδεμένες κορυφές : 2 βάρη συνδεδεμένων ακμών: 1.00 Κορυφή #2 βαθμός κορυφής: 2 συνδεδεμένες κορυφές : 1 2 βάρη συνδεδεμένων ακμών: 0.95 1.03 Press any key to continue . . . Επεξ/σία 18 Μαΐου 2013 από temp_
rafail1994 Δημοσ. 17 Μαΐου 2013 Μέλος Δημοσ. 17 Μαΐου 2013 Ο κώδικας θα διορθωθεί εννοείτε απλώς έκανα στα "γρήγορα" την βάση του πάλι. Την errexit() συνάρτηση με ποιον τρόπο την χρησιμοποιώ ; δηλαδή είναι σε κάποια βιβλιοθήκη γιατί έτσι όπως την έβαλα δεν δούλεψε Ευχαριστώ και πάλι !!
temp_ Δημοσ. 17 Μαΐου 2013 Δημοσ. 17 Μαΐου 2013 Ο κώδικας θα διορθωθεί εννοείτε απλώς έκανα στα "γρήγορα" την βάση του πάλι. Την errexit() συνάρτηση με ποιον τρόπο την χρησιμοποιώ ; δηλαδή είναι σε κάποια βιβλιοθήκη γιατί έτσι όπως την έβαλα δεν δούλεψε Ευχαριστώ και πάλι !! Παρακαλώ, αλλά προφανώς δεν κοίταξες τον κώδικα που έδωσα στο προηγούμενο ποστ. Εκεί την έχω γράψει την errexit(), δεν υπάρχει σε κάποια βιβλιοθήκη, τώρα την έγραψα. Αν είναι να την χρησιμοποιήσεις, σημείωσε πως δεν σου δίνει την ευκαιρία να κάνεις free() την μνήμη που έχει δεσμεύσει το πρόγραμμά σου. Πρέπει πρώτα να κάνεις αποδέσμευση και μετά να την καλέσεις... όπως δηλαδή και στην κανονική exit().
rafail1994 Δημοσ. 25 Μαΐου 2013 Μέλος Δημοσ. 25 Μαΐου 2013 Επειδή έχω μπερδευτεί λίγο .Έχω μια δομή που αυτής της μορφής γίνεται τα στοιχειά αυτής της δομής να αποθηκευτούν σε αρχείο ,ώστε όταν ξανά ανοίξω το πρόγραμμα να τα ανακτήσω,διαχειριστώ ; struct recordcustomer { char CustomerName[20]; //¼íïìá ðåëÜôç int CustomerCode; //Êùäéêüò ðåëÜôç int ProductCode; //Êùäéêüò ðñïúüíôïò float OrderQuantity; //Ðïóüôçôá ðáñáããåëßáò };
imitheos Δημοσ. 25 Μαΐου 2013 Δημοσ. 25 Μαΐου 2013 Επειδή έχω μπερδευτεί λίγο .Έχω μια δομή που αυτής της μορφής γίνεται τα στοιχειά αυτής της δομής να αποθηκευτούν σε αρχείο ,ώστε όταν ξανά ανοίξω το πρόγραμμα να τα ανακτήσω,διαχειριστώ ; struct recordcustomer { char CustomerName[20]; //¼íïìá ðåëÜôç int CustomerCode; //Êùäéêüò ðåëÜôç int ProductCode; //Êùäéêüò ðñïúüíôïò float OrderQuantity; //Ðïóüôçôá ðáñáããåëßáò }; Τα πάντα γίνονται οπότε φυσικά και γίνεται Το πώς γίνεται τώρα σηκώνει συζήτηση. Εφόσον η δομή σου δεν έχει δείκτες (πίνακας δεν μας πειράζει), ο πιο εύκολος τρόπος είναι να γράψεις κατευθείαν ολόκληρη τη δομή στο αρχείο με fwrite και να διαβαστεί με fread. Αυτή η τακτική όμως έχει ένα κακό. Οι compilers βάζουν padding στις δομές για να είναι πιο γρήγορη η προσπέλαση (ή γιατί είναι αναγκασμένοι από την αρχιτεκτονική) οπότε για να διαβαστεί σωστά η δομή θα πρέπει το εκτελέσιμο να έχει παραχθεί για την ίδια αρχιτεκτονική και από τον ίδιο compiler (ακόμη και διαφορετική έκδοση του ίδιου compiler μπορεί να βάλει διαφορετικό padding). Μια άλλη μέθοδος είναι να χρησιμοποιήσεις πάλι την fwrite αλλά να γράψεις στο αρχείο ένα-ένα τα πεδία. Έτσι γλυτώνεις το padding αλλά πάλι μπορεί να έχεις πρόβλημα λόγω διαφορετικού μεγέθους των τύπων (πχ 32bit και 64bit) ή διαφορετικού endianness (big vs little). Καλύτερη μέθοδος είναι να επιλέξεις ένα format και να γραφτούν όλα σαν strings. Δηλαδή αν το ProductCode είναι ο αριθμός 351374 να γράψεις τους χαρακτήρες '3','5','1','3','7','4','0',κτλ ή να επιλέξεις ένα format που είναι για αυτή τη δουλειά όπως yaml, json, κτλ. Ανάλογα με το τι σκοπό έχεις επιλέγεις και την αντίστοιχη λύση. Αν πρόκειται για κάτι σοβαρό εννοείται πως θες μια δόκιμη λύση. Αν πρόκειται για μια άσκηση που θα την τρέξει μια φορά ο καθηγητής και δεν θα ξανα-ασχοληθεί κανείς γράψε με τη μία με fwrite.
rafail1994 Δημοσ. 25 Μαΐου 2013 Μέλος Δημοσ. 25 Μαΐου 2013 Μάλιστα όποτε ξεκινάμε το διάβασμα με το πως θα γίνει αφού γίνεται Πρόκειται για άσκηση όποτε δεν νομίζω να αξίζει να ταλαιπωρηθώ τόσο πολύ για να δουλεύει πάντα σωστά (άλλωστε ο καθηγητής βλέπει μόνο τον κώδικα δεν τα τρέχει ποτέ τα προγράμματα)
imitheos Δημοσ. 26 Μαΐου 2013 Δημοσ. 26 Μαΐου 2013 Μάλιστα όποτε ξεκινάμε το διάβασμα με το πως θα γίνει αφού γίνεται Πρόκειται για άσκηση όποτε δεν νομίζω να αξίζει να ταλαιπωρηθώ τόσο πολύ για να δουλεύει πάντα σωστά (άλλωστε ο καθηγητής βλέπει μόνο τον κώδικα δεν τα τρέχει ποτέ τα προγράμματα) Ε δεν χρειάζεται διάβασμα αφού θα το κάνεις με το σύντομο τρόπο. Μια fwrite θα τρέξεις. Τώρα που έχω χρόνο ας εξηγήσω λίγο καλύτερα αυτά που είπα χτες. struct rc_s { char name[20]; int myint; char myc; float myfl; }; int write_rc(FILE *f, struct rc_s rc) { fwrite(rc.name, sizeof(rc.name), 1, f); fprintf(f, "%20d", rc.myint); fprintf(f, "%c", rc.myc); fprintf(f, "%10.10f", rc.myfl); } Όταν το πρόγραμμα είναι τέτοιο που να μην δικαιολογεί την χρήση ενός serialization προτύπου όπως το yaml, τότε όπως είπα πριν μπορούν να γραφτούν όλα σαν strings. Η συνάρτηση write_rc, όπως βλέπεις, γράφει τα πάντα σαν strings οπότε δεν έχεις ούτε προβλήματα λόγω padding ούτε λόγω endianness. Το μόνο μειονέκτημα είναι ότι καταναλώνει παραπάνω χώρο στο αρχείο (λόγω του format %20d αν έχουμε τον αριθμό 6 αυτός θα γραφεί ως 19 space + το χαρακτήρα '6') αλλά για τόσο λίγο δεν έγινε και τίποτα. Επίσης συχνά σε προγράμματα χρησιμοποιείται και μια πιο αυτοματοποιημένη μέθοδος που εκμεταλλεύεται το offsetof macro σε συνδυασμό με function pointers αλλά για τέτοιες απλές ασκήσεις είναι πολύ overkill. Το έγραψα εγκυκλοπαιδικά για να δεις μια άλλη λύση. Αν δεν έχετε διδαχθεί ακόμη αυτές τις έννοιες τότε αγνόησε το για να μην μπερδευτείς. struct rc_s { char name[20]; int myint; char myc; float myfl; }; enum mtype { MSTR, MINT, MCHAR, MFLOAT, MEND }; struct rc_desc_s { char *desc; enum mtype mtype; size_t offset; }; static const struct rc_desc_s rc_desc[] = { { "MY NAME", MSTR, offsetof(struct rc_s, name) }, { "MY INTEGER", MINT, offsetof(struct rc_s, myint) }, { "MY CHARACTER", MCHAR, offsetof(struct rc_s, myc) }, { "MY FLOAT", MFLOAT, offsetof(struct rc_s, myfl) }, { NULL, MEND, 0 }, }; Εκτός από τη δομή μας ορίζουμε και μία δεύτερη δομή η οποία έχει ένα πεδίο το οποίο δηλώνει τι τύπο έχει το κάθε μέλος της δομής μας (συνήθως enum γιατί είναι το πιο βολικό) και ένα πεδίο offset το οποίο δείχνει που βρίσκεται το μέλος σε σχέση με την αρχή της δομής. Αυτά τα δύο είναι απαραίτητα αλλά μπορούμε να βάλουμε και ό,τι άλλο θεωρούμε χρήσιμο όπως έβαλα ένα string με τη περιγραφή του κάθε μέλους ή τις επιτρεπτές τιμές ή οτιδήποτε. Έπειτα ορίζουμε ένα πίνακα που λέει τι είναι το κάθε μέλος και τι offset έχει με τη χρήση του macro offsetof. Τι μας βοηθάει αυτό ? Μπορούμε να γράψουμε generic κώδικα που να μην τον νοιάζει πως είναι η δομή μας και απλά να δουλεύει με τον κάθε τύπο. Έπειτα αυτός ο κώδικας θα καλείται προγραμματιστικά. int write_rc(FILE *f, struct rc_s *rc, const struct rc_desc_s rc_desc[]) { int i = 0; while (rc_desc[i].desc != NULL) { ptr[rc_desc[i].mtype](f, rc, rc_desc[i].offset); i++; } return 0; } Ορίστε η νέα συνάρτηση εγγραφής της δομής. Αντί να γράφουμε χειροκίνητα το κάθε πεδίο όπως παλιά, ανατρέχουμε τον πίνακα της περιγραφής για όσο υπάρχουν στοιχεία και καλούμε μια συνάρτηση. Ο ptr είναι ένας πίνακας από function pointers και ορίζεται ως εξής: typedef int (*write_ptr)(FILE *f, struct rc_s *rc, size_t offset); static const write_ptr ptr[MEND + 1] = { [MSTR] = write_str, [MINT] = write_int, [MCHAR] = write_char, [MFLOAT] = write_float, [MEND] = write_end, }; Για κάθε τύπο που έχουμε ορίζεται και μία συνάρτηση η οποία δέχεται ακριβώς τα ίδια ορίσματα και εκτελεί τη σωστή διαδικασία για το κάθε τύπο. Έτσι όταν ένα στοιχείο του πίνακα περιγραφή έχει mtype MINT, τότε θα εκτελεστεί η συνάρτηση που βρίσκεται στη θέση ptr[MINT] δηλαδή η write_int. int write_int(FILE *f, struct rc_s *rc, size_t offset) { int *i; i = (int *)((char *)rc + offset); fprintf(f,"%20d", *i); return 0; } Μία από αυτές τις συναρτήσεις είναι η παραπάνω. Όπως βλέπουμε παίρνει το δείκτη της δομής και (αφού τον μεταχειριστεί σαν να είναι δείκτης σε char ώστε να έχουμε σωστή αριθμητική) του προσθέτει το ανάλογο offset. Έτσι ο δείκτης πλέον δείχνει στο σωστό μέλος της δομής το οποίο εγγράφεται στο αρχείο. Φυσικά σε μια τέτοια άσκηση δεν κερδίζουμε τίποτα να γράψουμε όλο αυτό το κατεβατό με 10 συναρτήσεις write_τάδε. Σε μεγάλα προγράμματα όμως είναι συνήθης τακτική γιατί άπαξ και το γράψεις μια φορά μετά όλα γίνονται προγραμματιστικά και όχι χειροκίνητα. Αν για παράδειγμα θέλεις να προσθέσεις ένα νέο πεδίο απλά το προσθέτεις στη δομή και στη περιγραφή αντί να πρέπει να προσθέσεις κώδικα σε 15 σημεία του προγράμματος.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα