jimbakl Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 καλησπέρα σας, έχω τον παρακάτω κώδικα, > void calloc2d(double** array, int r, int c){ int i; array=(double**)calloc(r,sizeof(double*)); if(array==NULL){ printf("\nInsufficient Memory\n"); } for(i=0;i<r;r++){ array[i]=(double*)calloc(c,sizeof(double)); } } και θέλω μέσα στην main να την καλώ την συνάρτηση, γράφω δλδ double **dis; calloc2d(dis,n,n); αλλά μου βγάζει σφάλμα λέγοντας ότι δεν είναι ορισμένος ο πίνακας dis και ότι Error 15 error C2109: subscript requires array or pointer type ξέρετε ποιο είναι το λάθος; Ώρες ώρες είναι να τρελαίνεσαι με αυτή την C, το "λάθος" ήταν ότι το double **dis το είχα ορίσει ακριβώς πάνω από το κάλεσμα της συνάρτησης, μόλις το πήρα από εκεί και το έβαλα στην αρχή της main μαζί με τα άλλα declarations έπαιξε, και βγάζει το εξής σφάλμα... Run-Time Check Failure #3 - The variable 'dis' is being used without being initialized. ενώ την αρχικοποιώ μετά αν εννοεί αυτό που καταλαβαίνω. >calloc2d(dis,n,n); //=============================================== //Initialize dis[][] for(i=0;i<n;i++){ for(j=0;j<n;j++){ dis[i][j]=99999; } }
imitheos Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 καλησπέρα σας, έχω τον παρακάτω κώδικα, > void calloc2d(double** array, int r, int c){ int i; array=(double**)calloc(r,sizeof(double*)); if(array==NULL){ printf("\nInsufficient Memory\n"); } for(i=0;i<r;r++){ array[i]=(double*)calloc(c,sizeof(double)); } } Καταρχήν, το for σου είναι λάθος. Θέλεις i++ αντί για r++. Ώρες ώρες είναι να τρελαίνεσαι με αυτή την C, το "λάθος" ήταν ότι το double **dis το είχα ορίσει ακριβώς πάνω από το κάλεσμα της συνάρτησης, μόλις το πήρα από εκεί και το έβαλα στην αρχή της main μαζί με τα άλλα declarations έπαιξε, και βγάζει το εξής σφάλμα... Δεν φταίει η C αυτή καθεαυτή αλλά μάλλον ο compiler σου. Η C89 απαιτούσε οι δηλώσεις μεταβλητών να βρίσκονται όχι απαραίτητα στην αρχή της main αλλά απαραίτητα πριν να εκτελέσεις κώδικα. Η C99 δεν έχει αυτό τον περιορισμό αλλά ίσως ο compiler σου δεν την έχει υλοποιήσει. και θέλω μέσα στην main να την καλώ την συνάρτηση, γράφω δλδ double **dis; calloc2d(dis,n,n); Ώρες ώρες είναι να τρελαίνεσαι με αυτή την C, το "λάθος" ήταν ότι το double **dis το είχα ορίσει ακριβώς πάνω από το κάλεσμα της συνάρτησης, μόλις το πήρα από εκεί και το έβαλα στην αρχή της main μαζί με τα άλλα declarations έπαιξε, και βγάζει το εξής σφάλμα... Run-Time Check Failure #3 - The variable 'dis' is being used without being initialized. ενώ την αρχικοποιώ μετά αν εννοεί αυτό που καταλαβαίνω. >calloc2d(dis,n,n); //=============================================== //Initialize dis[][] for(i=0;i<n;i++){ for(j=0;j<n;j++){ dis[i][j]=99999; } } > #include <stdio.h> #include <stdlib.h> void calloc2d(double **array, int r, int c); int main(void) { double **dis = NULL;; printf("dis before calloc = %p\n", (void *)dis); calloc2d(dis, 5, 5); printf("dis after calloc = %p\n", (void *)dis); return 0; } void calloc2d(double **array, int r, int c){ int i; array=(double**)calloc(r,sizeof(double*)); if(array==NULL){ printf("\nInsufficient Memory\n"); } printf("dis in calloc = %p\n", (void *)array); for(i=0;i<r;i++){ array[i]=(double*)calloc(c,sizeof(double)); } } Δες το παραπάνω πρόγραμμα που είναι ίδιο με το δικό σου αλλά λίγο αλλαγμένο ώστε να εμφανίζει την διεύθυνση της dis σε διάφορα στάδια. Το αποτέλεσμα που παίρνουμε όταν τρέχει είναι το παρακάτω: > dis before calloc = (nil) dis in calloc = 0x179c010 dis after calloc = (nil) Όπως βλέπεις, η dis όντως δεν αρχικοποιείται. Ένας τρόπος να κάνεις αυτό που θέλεις είναι αντί να περνάς την dis ως παράμετρο στην συνάρτηση, να περνάς μόνο τα n,c και να επιστρέφεις το dis.
moukoublen Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 Όπως βλέπεις, η dis όντως δεν αρχικοποιείται. Ένας τρόπος να κάνεις αυτό που θέλεις είναι αντί να περνάς την dis ως παράμετρο στην συνάρτηση, να περνάς μόνο τα n,c και να επιστρέφεις το dis. Ένας άλλος είναι να περάσεις τη διεύθυνση μνήμης του διπλού pointer (τριπλός pointer) > #include <stdio.h> #include <stdlib.h> void calloc2d(double ***array, int r, int c); int main(void) { double **dis = NULL;; printf("dis before calloc = %p\n", (void *)dis); calloc2d(&dis, 5, 5); printf("dis after calloc = %p\n", (void *)dis); return 0; } void calloc2d(double ***array, int r, int c){ int i; *array = (double**)calloc(r,sizeof(double*)); if((*array)==NULL){ printf("\nInsufficient Memory\n"); } printf("dis in calloc = %p\n", (void *)(*array)); for(i=0;i<r;i++){ (*array)[i]=(double*)calloc(c,sizeof(double)); } } Αν και η πρόταση του imitheos είναι καλύτερη γιατί κερδίζει σε απλότητα. Σε τι περιβάλλον αναπτύσσεις? (IDE, compiler)
migf1 Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 Ένας άλλος είναι να περάσεις τη διεύθυνση μνήμης του διπλού pointer (τριπλός pointer) ... Αν και η πρόταση του imitheos είναι καλύτερη γιατί κερδίζει σε απλότητα. ... Κι αυτός καλός τρόπος είναι, δηλαδή το "by reference" πέρασμα του dis στην calloc2d(). Και είναι καλό να συνοδεύεται κι από ένα "by reference" πέρασμα σε μια συνάρτηση απελευθέρωσης, που εκτός από free() θα τον κάνει και NULL. Παρεμπιπτόντως, η calloc2d() του αρχικού post είναι ημιτελής (προβληματική). Δεν ελέγχει για αποτυχία της calloc() μέσα στο for-loop και προφανώς δεν κάνει και cleanup σε τέτοια περίπτωση (memory leak).
V.I.Smirnov Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 Δοθείσης της ευκαιρίας, ας μου επιτραπεί να παρατηρήσω τα εξής. Η δέσμευση μνήμης χωριστά για κάθε γραμμή, δηλ. με χρήση του > for(i=0;i<r;i++) (*array)[i]=(double*)calloc(c,sizeof(double)); είναι γενικά κακή πρακτική διότι η μνήμη μπορεί να δεσμεύεται κατακερματισμένα. Όταν σαρώνεται ο πίνακας, η ανάγνωση τυχόν τμημάτων του (γραμμές) που βρίσκονται σε διαφορετικές περιοχές της μνήμης επιφέρει καθυστέρηση. Επιπλέον, ο κατακερματισμός έχει ως αποτέλεσμα τμήματα της μνήμης να μην μπορούν να χρησιμοποιηθούν πλήρως. Αυτό ήταν ένα πρόβλημα που τώρα λύνεται (νομίζω) εμμέσως από το λειτουργικό σύστημα... Είναι καλύτερο να δεσμεύεται μονομιάς όλη η απαιτούμενη μνήμη με μια εντολή του τύπου > s = r*c; Tipus* p = (Tipus*)calloc(s, sizeof(Tipus)); και μετά με ένα βρόχο να δείχνεται η αρχή κάθε σειράς στην δεσμευμένη περιοχή με (κάπως έτσι) >for (i = 0; i < r; i++) { array[i] = p; p += c; } Τοιουτοτρόπως, αργότερα στο πρόγραμμα, για ανάθεση τιμών ή αντιγραφή του πίνακα, μπορούν να χρησιμοποιηθούν και εντολές memset ή memcpy που είναι λιτές και σαφώς πιο γρήγορες από τους βρόγχους. Αν ο πίνακας δεν είναι σίγουρα μαζεμένος σε ένα μόνον μέρος της μνήμης αντί εδώ κι εκεί, αυτά δεν μπορούν να γίνουν. Aπό άποψη βελτιστοποίησης, ακόμη καλύτερο αποτέλεσμα επιτυγχάνεται αν η δέσμευση μνήμης γίνεται πάντα για πλήθος στοιχείων που είναι (ακέραιο) πολλαπλάσιο αυτών της μια γραμμής. (Το έχω δει έτσι αλλά χρηστικά είναι κάπως μπελάς είναι αλήθεια...) -
migf1 Δημοσ. 26 Μαρτίου 2012 Δημοσ. 26 Μαρτίου 2012 +1 στον φίλο Smirnov. Έχουμε αναφερθεί αρκετές φορές σε αυτό το θέμα, και με παραδείγματα... αυτός είναι έτσι κι αλλιώς ο τρόπος που διαχειρίζεται ο compiler τους πολυδιάστατους, στατικούς πίνακες. Μονοκόμματη δέσμευση μνήμης ίσης με NROWS * NCOLS *sizeof(element) δηλαδή σαν να ήταν 1d, και κατόπιν αν n είναι ο indexer του 1d πίνακα, τότε τα: >i = n / NCOLS; j = n % NCOLS; σου δίνουν την τρέχουσα γραμμή και στήλη, αντίστοιχα, του εκάστοτε n. Αντίστροφα, το... >n = i * NCOLS + j σου υπολογίζει το n αν ξέρεις την γραμμή i και την στήλη j. Από την άλλη μεριά, η δυναμική δέσμευση ενός 1d πίνακα από NROWS δείκτες μας επιτρέπει να "κολλήσουμε" πάνω τους (επίσης δυναμικά) γραμμές ανομοιογενούς μήκους, που μπορεί να μας εξοικονομήσει πολλή μνήμη!
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα