migf1 Δημοσ. 11 Μαΐου 2011 Δημοσ. 11 Μαΐου 2011 (επεξεργασμένο) Δυστυχώς είναι αλγοριθμικό το πρόβλημα και εγώ έχω σκουριάσει σε αυτά, θέλει ψάξιμο. Ελπίζω να σε βοηθήσει κάποιος που τα έχει πιο φρέσκα από εμένα. EDIT: άτιμη κούραση! Μια χαρά δουλεύει ο κώδικας τελικά, είχα κάνει μια αβλεψία στο malloc() των B[ i ], όπου αντί να μετράω μέχρι το ncolumns μέτραγα μέχρι το nrows (άτιμο copy & paste) Πήγα να τρελαθώ μιλάμε, κοίταγα και ξανακοίταγα τον κώδικα της αντιγραφής γιατί εκεί νόμιζα πως είχα λάθος και δεν έβρισκα κάτι, αλλά στο run μου χτύπαγε! Τέσπα,τέλος καλό όλα καλά! Εγώ σου παραθέτω τον κώδικα όπου έχω βάλει ότι έχει να κάνει με τη γλώσσα αυτή κάθε αυτή, μαζί με επεξηγηματικά σχόλια κι απομόνωσα το επίμαχο σημείο μέσα σε σχόλια. Π.χ. στον δικό σου κώδικα δεν κάνεις free() στο τέλος τη μνήμη που έχεις κάνεις malloc() στην αρχή, ούτε ελέγχεις αν πέτυχε το κάθε σου malloc(). > #include <stdio.h> #include <stdlib.h> #include <time.h> #define out_of_memory " *** termatismos programmatos (aneparkhs mnhmh) *** " int main( void ) { int **A, **B; int nrows, ncolumns; register int i,j; printf("Dwse arithmo grammwn, sthlwn\n"); scanf("%d %d", &nrows, &ncolumns); // δέσμευση μνήμης για τον A ως πίνακα nrows δεικτών A = (int **) malloc( nrows * sizeof(int *) ); if ( A == NULL ) { // αποτυχία του malloc() printf("\n%s\npathste ENTER", out_of_memory); fflush(stdin); getchar(); return 1; // τερματισμός προγράμματος } // δέσμευση μνήμης για τους nrows δείκτες του Α, ως πίνακες ncolumns ακεραίων for (i=0; i < nrows; i++) { A[i] = (int*) malloc( ncolumns * sizeof(int) ); if ( A[i] == NULL ) // αποτυχία του malloc() { for (j=(i-1); j>=0; j--) // αποδέσμευση των προηγούμενων free( A[j] ); printf("\n%s\npathste ENTER", out_of_memory); fflush(stdin); getchar(); return 1; // τερματισμός προγράμματος } } /* σε αυτό το σημείο έχουμε φτιάξει χώρο για A[nrows][ncolumns] */ // δέσμευση μνήμης για τον B ως πίνακα ncolumns δεικτών B = (int **) malloc(ncolumns * sizeof(int *) ); if ( B == NULL ) { // αποτυχία του malloc() printf("\n%s\npathste ENTER", out_of_memory); fflush(stdin); getchar(); return 1; // τερματισμός προγράμματος } // δέσμευση μνήμης για τους ncolumns δείκτες του B, ως πίνακες nrows ακεραίων for (i=0; i < ncolumns ; i++) { B[i] = (int *) malloc(nrows * sizeof(int) ); if ( B[i] == NULL ) // αποτυχία του malloc() { for (j=(i-1); j>=0; j--) // αποδέσμευση των προηγούμενων free( A[j] ); printf("\n%s\npathste ENTER", out_of_memory); fflush(stdin); getchar(); // τερματισμός προγράμματος return 1; } } /* σε αυτό το σημείο έχουμε φτιάξει χώρο για B[ncolumns][nrows] */ // διάβασμα τιμών για όλα τα στοιχεία του A for (i=0; i<nrows; i++) { for (j=0; j<ncolumns; j++) { A[i][j] = (i * ncolumns + j) + 1; /* Για να μη κάθομαι να περνάω τιμές με το χέρι από το πληκτρολόγιο * έβαλα την παραπάνω γραμμή που δημιουργεί τιμές αυτόματα ξεκινώντας * από το 1 και ανεβάζοντας κατά 1 σε κάθε επόμενο στοιχείο. * * Αν θέλετε να δημιουργούνται τυχαίες τιμές στον πίνακα Α * από 1 έως 1000, αλλάξτε την παραπάνω γραμμή με αυτήν: * * A[i][j] = (rand() % 1000) + 1; * * και προσθέστε έξω από το for-loop του i (ή στην αρχή του * προγράμματός) αυτήν εδώ τη γραμμή: * * srand( time(NULL) ); * * Αν θέλετε τις τιμές να τις δίνει χρήστης, τότε χρησιμοποιήστε * τις 2 γραμμές που ακολουθούν: * * printf("Dwste timh gia to stoixeio A[%d,%d]: ", i,j); * scanf("%d", &A[i][j]); */ } } /* εκτύπωση του πίνακα Α */ puts("\nPinakas A"); puts("---------"); for (i=0; i<nrows; i++) { for (j=0; j<ncolumns; j++) printf("%-5d ", A[i][j]); putchar('\n'); } putchar('\n'); /* αντιγραφή του Α στον Β, με αναστροφή γραμμών σε στήλες */ for (i=0; i < nrows; i++) for (j=0; j < ncolumns; j++) B[j][i] = A[i][j]; /* εκτύπωση του πίνακα Β */ puts("\nPinakas B"); puts("---------"); for (i=0; i<ncolumns; i++) { for (j=0; j<nrows; j++) printf("%-5d ", B[i][j]); putchar('\n'); } putchar('\n'); /* Απελευθέρωση της μνήμης που δεσμέυσαμε για τους πίνακες */ // απελευθέρωση του Α for (i=0; i<nrows; i++) free( A[i] ); free( A ); // απελευθέρωση του B for (i=0; i<ncolumns; i++) free( B[i] ); free( B ); puts("pathste ENTER gia ejodo"); fflush(stdin); getchar(); return 0; } Επεξ/σία 12 Μαΐου 2011 από migf1
migf1 Δημοσ. 11 Μαΐου 2011 Δημοσ. 11 Μαΐου 2011 Βέβαια, υπάρχει τρικ, αλλά είναι π@υστιά και και δεν λέει. Οι πίνακες μπορεί να δηλωθούν και να γίνουν malloc() ως μονοδιάστατοι: > int *A, *B; int n, nrows, ncolumns; // διάβασε εδώ τα nrows, ncolumns A = (int *) malloc( (nrows*ncolumns) * sizeof(int) ); B = (int *) malloc( (nrows*ncolumns) * sizeof(int) ); και μετά να τους προσπελάσεις μονοδιάστατα με το n και να τους τυπώνεις (διαχειρίζεσαι) κατά βούληση είτε ως [nrows][ncolumns] είτε ως [ncolumns][nrows]. Αν δηλαδή προσπελάσεις μονοδιάστατα τον A και A[n] είναι το n στοιχείο του, τότε οι παρακάτω τύποι αντιστοιχούν το n σε 2-διάστατους δείκτες i,j ως εξής: > i = n / ncolumns; j = n % ncolumns; Όπως επισης, μπορείς να προσπελάσεις τον πίνακα χρησιμοποιώντας 2-διάστατους δείκτες i,j και να υπολογίζεις το αντίστοιχο n ως εξής: > n = i * ncolumns + j; Γράφοντας λοιπόν κάτι σαν αυτό: > for (i=0; i<nrows; i++) { for (j=0; j<ncolumns; j++) { int n = i * ncolumns + j; printf("A(%d,%d)=%d ", i, j, A[n] ); } putchar('\n'); // newline μετά από κάθε γραμμή } τυπώνεις στην οθόνη τον Α ως Α[nrows][ncolumns]. Αλλάζοντας τη σειρά των for-loops (π.χ. κάνοντας εξωτερικό το εσωτερικό) τυπώνεις τον A ως A[ncolumns][nrows] Αντίστοιχα θα μπορούσες να γράψεις αυτό: > for (n=0; n < (nrows * ncolumns); n++) { int i= n / ncolumns; int j = n % ncolumns; printf("A(%d,%d)=%d ", i, j, A[n] ); if ( j == ncolumns ) putchar('\n'); // newline μετά από κάθε γραμμή } για να τον τυπώσεις ως A[nrows][ncolumns] (το τύπωμα ως A[ncolumns][nrows] το αφήνω ως... άσκηση ). ------------------------------------------------------------------------------------- Αυτός είναι ο τρόπος που χρησιμοποιεί και η ίδια η C για την προσπέλαση των πινάκων, όταν τους ορίσει κανείς ως arrays από την αρχή (και όχι ως pointers). Και πολυδιάστατους πίνακες να ορίσουμε (π.χ. array[5][10[6]), εσωτερικά μετατρέπονται σε μονοδιάστατους με την λογική που παρέθεσα παραπάνω στην προσπέλασή τους. EDIT: Problem solved, είχα κάνει μια μικρή... λαλακιούλα Την διόρθωσα κι όλα ΟΚ τώρα, ενημέρωσα το σχετικό post (εννοείται και τον κώδικα). Καλού-κακού τσέκαρέ τον όμως σε διάφορες περιπτώσεις input. Πρόσθεσα επίσης απελευθέρωση μνήμης των προηγούμενων A και B αν "χτυπήσει" κάποιο από αυτά, καθώς επίσης και τη δυνατότητα να γεμίζει αυτόματα ο πίνακας Α με τυχαίες τιμές από το 1 έως το 1000.
personGR Δημοσ. 12 Μαΐου 2011 Δημοσ. 12 Μαΐου 2011 Ok, migf1, χρωστάω χάρη. Απλά θέλω να μου εξηγήσεις λίγο τα περί αποτυχίας της malloc. Στο βιβλίο που μας έδωσαν δεν έχει κάτι τέτοιο (στο κεφάλαιο των πινάκων που την εισάγει), και λίγο που έψαξα την fflush() είδα πως έχει να κάνει με files (ή απλά τα'χω παίξει από την κούραση) κάτι που δεν έχω κάτσει να ασχοληθώ ακόμα. Άμα δεν βάλω τα κομμάτια αυτά του κώδικα στο πρόγραμμα, παίζει να γίνει κάτι τραγικό; BTW, στο δικό μου πρόγραμμα, πέρα από τα λάθη στη χρήση της malloc, το λάθος που προκαλούσε το κόλλημα είναι πως μπέρδεψα τις αντίστοιχες nrows/nocolumns στη δεύσμευση των A και B.
migf1 Δημοσ. 13 Μαΐου 2011 Δημοσ. 13 Μαΐου 2011 Η malloc() δεσμεύει ένα κομμάτι συνεχόμενης μνήμης (μεγέθους τόσων bytes όσων της έχεις ορίσει μέσα στη παρένθεσή της όταν την καλείς) το οποίο κομμάτι μνήμης εφόσον βρεθεί "εκχωρείται" στον δείκτη που κάνεις malloc. Σπάνια μεν, αλλά υπάρχει περίπτωση να μην υπάρχει τέτοιο συνεχόμενο κομμάτι μνήμης ελεύθερο. Σε αυτή την περίπτωση αρχίζει και κάνει εσωτερικά κάτι δικά της κόλπα (κόβει, ξηλώνει, ράβει, ενώνει κλπ κομμάτια μνήμης) προκειμένου να διεκπεραιώσει την αποστολή της. Αν πάλι όμως δεν μπορέσει, σου επιστρέφει την τιμή NULL (που στη C γενικώς σημαίνει αποτυχία όταν επιστρέφεται από συνάρτηση ή σημαίνει "πουθενά" όταν το εκχωρείς εσύ χειροκίνητα σε κάποιον pointer). Το NULL είναι μια σταθερά τύπου long και τιμή ίση με 0L ή (void *)0 Συνήθως ορίζεται μέσα στο stdio.h ή στο stdef.h Όταν λοιπόν χρησιμοποιείς την malloc() οφείλεις κατόπιν να ελέγχεις αν σου επέστρεψε NULL, διότι άμα σου επέστρεψε NULL σημαίνει πως δεν εκχωρήθηκε η μνήμη που ζήτησες για τον δείκτη σου. Άρα και ο δείκτης δείχνει στο "πουθενά", οπότε άμα πας εσύ μετά να τον χρησιμοποιήσεις νομίζοντας πως όλα είναι οκ, θα "φας" run-time error. Το ίδιο ακριβώς συμβαίνει και με την calloc() (αδερφάκι της malloc() που εκτός από τη δέσμευση μνήμης την αρχικοποιεί κιόλας με την τιμή 0 σε όλα της τα bytes). Γενικώς είναι πάντα καλύτερα να χρησιμοποιείς την calloc() αντί της malloc(), αλλά υποθέτω θα σας το πουν πιο μετά. Τώρα, τόσο η malloc() όσο και η calloc() (όπως και η realloc()) τη μνήμη τη δεσμεύουν μόνιμα, μέχρι να την αποδεσμεύσεις εσύ με την συνάρτηση free(). Θεωρητικά η μνήμη απελευθερώνεται όταν τερματίσει το πρόγραμμά σου, αλλά δεν μπορείς να βασιστείς στη θεωρία γιατί βρίσκεται σε άμεση εξάρτηση με το λειτουργικό σύστημα στο οποίο τρέχει το πρόγραμμά σου. Είναι γνωστές μάλιστα ιστορίες "τρόμου" στο παρελθόν από memory leaks, ακριβώς από προγράμματα που τερμάτιζαν χωρίς να απελευθερώνουν τη μνήμη που είχαν δεσμεύσει δυναμικά. Οπότε, για κάθε malloc() ή calloc() θα πρέπει να φροντίζεις να έχεις αντίστοιχα free() πριν τερματίσεις το πρόγραμμά σου. Σου δίνω και 2 links (Αγγλικά) για περαιτέρω πληροφορίες, ένα... light κι ένα πιο αναλυτικό: link #1, link #2 Σχετικά με την fflush(), ναι είναι για αρχεία αλλά στη C τα stdin και stdout αντιστοιχούν στο πληκτρολόγιο και την οθόνη αντίστοιχα. Π.χ. αντί για printf("..."); μπορείς να χρησιμοποιήσεις την fprintf() που είναι για αρχεία, αλλά να της περάσεις το stdout ως file stream: fprintf(stdout, "..."); οπότε δουλεύει σαν την printf() Ομοίως και η fflush() ή οποία καθαρίζει από "σκουπίδια" το input stream ή εκτυπώνει το output stream. Εδώ μας ενδιαφέρει το standard input μόνο, δηλαδή το stdin. Το θέμα είναι πως όταν χρησιμοποιείς συναρτήσεις "διαβάσματος" όπως η scanf(), η getchar(), κλπ, περιμένουν να πατηθεί το ENTER πριν αρχίσουν να διαβάζουν αυτά που τους έχει πει να διαβάζουν. Αν λοιπόν ο χρήστης πριν πατήσει το ENTER έχει πληκτρολογήσει παραπάνω πράματα από αυτά που περιμένεις εσύ να διαβάσεις, τότε τα έξτρα μένουν μέσα στο input buffer. Το αποτέλεσμα είναι αν λίγο πιο κάτω εσύ ξανά καλέσεις μια συνάρτηση διαβάσματος, ενδέχεται (έως είναι σίγουρο, ανάλογα το λειτουργικό σύστημα και την πλατφόρμα) τα πρώτα πράματα που θα διαβάσεις να είναι τα "σκουπίδια" που έχουν μείνει στο input buffer από το προηγούμενο "διάβασμα". Οπότε πχ εσύ μπορεί τη 2η φορά να θες να διαβάσεις 3 πράματα από τον χρήστη, αλλά να τελικά διαβάσεις τα 2 τελευταία από τον χρήστη και το 1ο που θα διαβάσεις να είναι έξτρα πράμα που είχε πληκτρολογήσει παραπάνω στο προηγούμενο scanf σου. Οπότε για να έχεις το έχεις το κεφάλι σου ήσυχο, πριν από κάθε scanf() (ή οποιαδήποτε άλλη standard συνάρτηση διαβάσματος της C) να κάνεις πρώτα ένα fflush(stdin) ώστε να καθαρίζει το input buffer από προηγούμενα "σκουπίδια", ώστε να ξεκινάς τον χρήστη με καθαρό input buffer. Ελπίζω να μη σε μπέρδεψα χειρότερα! Ειδικά η scanf() αφήνει ένα σωρό "σκουπίδια" στο input buffer. Προσωπικά την αποφεύγω όσο μπορώ, είναι τελείως... παλιοσυνάρτηση Πάντως το fflush(stdin) μη το φοβάσαι (το fflush(stdout) να φοβάσαι). Όπου μπορείς να τα αποφεύγεις κάνε το. Στα δικά μου παραδείγματα όμως που βάζω απλά ένα getchar(); το fflush(stdin) πριν το καλέσω είναι τελείως άκακο. Και πριν από κάθε scanf() είναι απαραίτητο θα έλεγα εγώ! Δοκίμασε να κάνεις ένα πρόγραμμα με πολλά scanf() χωρίς fflush(stdin) μπροστά τους και μετά με fflush(stdin) πριν από κάθε scanf() να δεις τις διαφορές. Ειδικά αν σε κάθε scanf() πληκτρολογείς περισσότερα ή λιγότερα πράγματα από όσα περιμένει να διαβάσει.
personGR Δημοσ. 13 Ιουνίου 2011 Δημοσ. 13 Ιουνίου 2011 migf1, τώρα που έχω διαβάσει και για τα αρχεία και την fflush(), κατάλαβα 100% αυτά που έγραψες πιο πάνω, οπότε ...χίλια ευχαριστώ!!
Genevil Δημοσ. 28 Οκτωβρίου 2011 Δημοσ. 28 Οκτωβρίου 2011 Migf η fflush(stdin) για μένα λειτουργεί στα windows, αλλά όχι στα linux... Το έψαξα και βρήκα ότι είναι θέμα compiler, (εγώ πρέπει να χρησιμοποιήσω gcc)... Το θέμα είναι πως έχω μεγάλο πρόβλημα με τα σκουπίδια σε ένα πρόγραμμα και πρέπει να καθαρίσω το input buffer μου πριν χρησιμοποιήσω την fgets... Έχεις κάτι να προτείνεις; Ευχαριστώ...
migf1 Δημοσ. 28 Οκτωβρίου 2011 Δημοσ. 28 Οκτωβρίου 2011 Migf η fflush(stdin) για μένα λειτουργεί στα windows, αλλά όχι στα linux... Το έψαξα και βρήκα ότι είναι θέμα compiler, (εγώ πρέπει να χρησιμοποιήσω gcc)... Το θέμα είναι πως έχω μεγάλο πρόβλημα με τα σκουπίδια σε ένα πρόγραμμα και πρέπει να καθαρίσω το input buffer μου πριν χρησιμοποιήσω την fgets... Έχεις κάτι να προτείνεις; Ευχαριστώ... Όντως λειτουργεί μόνο σε windows, δυστυχώς! Έχω να σου προτείνω ναι, LIBS. Δεν έχω πολύ καιρό που ξεκίνησα να τη φτιάχνω, αλλά έχει ήδη αρκετές συναρτήσεις μέσα (αν και πολλές τις έχω ατεκμηρίωτες ακόμα). Θέλεις την: s_getflushed(). Αν δεν θες να χρησιμοποιήσεις τη βιβλιοθήκη, κάνε απλά copy & paste τη συγκεκριμένη συνάρτηση από τον πηγαίο κώδικα της LIBS στο δικό σου πρόγραμμα
migf1 Δημοσ. 28 Οκτωβρίου 2011 Δημοσ. 28 Οκτωβρίου 2011 ... <<Happy>> Την κρατάω αυτή τη σελίδα, thnx Απλά ούτε η σελίδα είναι έτοιμη ακόμα, κι αυτή τη φτιάχνω Όταν την έχω λειτουργική θα ενημερώσω (π.χ. το σχόλια δεν λειτουργούν ακόμα).
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα