alexc Δημοσ. 28 Μαρτίου 2013 Δημοσ. 28 Μαρτίου 2013 Καλησπέρα ζηάω και πάλι την βοήθεια σας γιατί αυτή η άσκηση μου έχει φάει το κεφάλι τόσες ώρες : Να υλοποιηθεί συνάρτηση που να δημιουργεί δυναμικά ένα πίνακα 20 ακεραίων με αρχική τιμή το 10 για όλα τα στοιχεία του. Η συνάρτηση δεν θα πρέπει να επιστρέφει τίποτα (τύπος συνάρτησης void) και η διεύθυσνη του δυναμικού πίνακα θα πρέπει να δίνεται στη main, μέσω κλήσης με αναφορά. Και η υλοποίηση μου: void foo(int **a) { int i; *a=(int*)malloc(10*sizeof(int)); for (i=0;i<10;i++) *a[i]=10; } main() { int *b,i; foo(&; for (i=0;i<10;i++) printf("%d",b[i]); system("pause"); } Δεν μπορώ να καταλάβω γιατί κρασάρει δεν μπορώ..... Δεσμεύω δυναμικά μνήμη και η διεύθυνση του πρώτου byte αυτής επιστρέφει στο *a. Κανονικά δεν θα έπρεπε να έχω πρόσβαση στον πίνακα απλά μέσω του a*??? Τι μου ξεφεύγει???
georgemarios Δημοσ. 28 Μαρτίου 2013 Δημοσ. 28 Μαρτίου 2013 [Edit] Ακυρο το ποστ μου, βραδιατικα βλεπω πουλακια...
Erevis Δημοσ. 28 Μαρτίου 2013 Δημοσ. 28 Μαρτίου 2013 Όταν κάνεις dereference τον a στη foo χρειάζεσαι παρενθέσεις λόγω προτεραιότητας τελεστών. void foo(int **a) { int i; *a=(int*)malloc(10*sizeof(int)); for (i=0;i<10;i++) (*a)[i]=10; } Στη main καλό θα ήταν να κάνεις free τη μνήμη που δέσμευσες όταν δε τη χρειάζεσαι πλέον. Κατά τ'άλλα σωστό σε βρίσκω! 1
migf1 Δημοσ. 28 Μαρτίου 2013 Δημοσ. 28 Μαρτίου 2013 Να σημειώσω πως δεν ακολούθησες την συμβουλή του ημίθεου να ΜΗΝ κάνεις cast την τιμή επιστροφής της malloc(), εφόσον μιλάμε για C και όχι για C++ (δες γιατί είναι καλύτερα να MHN κάνεις cast). Επίσης, θα πρέπει να κάνεις πάντα έλεγχο για το αν πέτυχε ή όχι η κλήση στην malloc()/calloc()/realloc(), αλλιώς ρισκάρεις κρασαρίσματα (π.χ. όταν δεν πετύχει η κλήση κι εσύ εξακολουθείς να χρησιμοποιείς κατόπιν τον δείκτη σαν να είχε πετύχει η κλήση... δλδ κάνεις dereference το NULL, που δίνει seg-fault. Σοφό και χρήσιμο είναι να κάνεις και sanity checks στα ορίσματα των συναρτήσεών σου, πριν αρχίσεις να τα χρησιμοποιείς. Π.χ. στον κώδικά σου, δοκίμασε να καλέσεις: foo( NULL ); θα κρασάρει. Παραθέτω και μια εναλλακτική υλοποίηση αμιγώς με δείκτες σε C99/C11 (δεν γνωρίζω σε τι έτος είστε και τι ακριβώς θέλει να εξασκήσετε με αυτή την άσκηση ο καθηγητής σας)... #include <stdio.h> #include <stdlib.h> /* --------------------------------------------- */ void arrInt20_print( int *arr ) { if ( !arr ) return; for ( int *walk=arr; walk < arr + 20; walk++ ) printf( "%d ", *walk ); putchar( '\n' ); } /* --------------------------------------------- */ void arrInt20_make( int **arr ) { if ( !arr ) return; if ( NULL == (*arr = malloc(20 * sizeof(int)) ) ) return; for ( int *walk = *arr; walk < *arr + 20; walk++ ) *walk = 10; } /* --------------------------------------------- */ int main( void ) { int *arr = NULL; arrInt20_make( &arr ); if ( arr ) { arrInt20_print( arr ); free( arr ); } system( "pause" ); /* Windows only */ return 0; } 1
Χάρι Πότερ Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 Διορθώστε με αν κάνω λάθος αλλα το πρώτο κελί σε έναν πίνακα δεν είναι δεικτης στα υπόλοιπα; Οπότε ποιο το νόημα να φτίαξεις δεικτη **(reference στον μονοδιαστατο(*) πίνακα) αφου έτσι κι αλλιως το *a απο μόνο του ειναι reference στα υπόλοιπα κελια του πινακα
bird Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 Διορθώστε με αν κάνω λάθος αλλα το πρώτο κελί σε έναν πίνακα δεν είναι δεικτης στα υπόλοιπα; Οπότε ποιο το νόημα να φτίαξεις δεικτη **(reference στον μονοδιαστατο(*) πίνακα) αφου έτσι κι αλλιως το *a απο μόνο του ειναι reference στα υπόλοιπα κελια του πινακα Το όνομα του πίνακα είναι δείκτης στο πρώτο στοιχείο του πίνακα δλδ: a==&a[0] 1
migf1 Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 (επεξεργασμένο) Όπως είπε και ο φίλος bird, το όνομα του πίνακα είναι δείκτης στο 1ο στοιχείο (κελί) του πίνακα.Η επιτυχημένη κλήση στη malloc() θα επιστρέψει μια διεύθυνση στην οποία βάζουμε να δείχνει ο δείκτης, λόγω της ανάθεσης που κάνουμε: arr = malloc(...); Η επιστρεφόμενη διεύθυνση από την malloc() ΔΕΝ είναι η διεύθυνση του δείκτη μας (είναι το νέο περιεχόμενο του δείκτη μας). Δηλαδή, ο δείκτης arr αυτός κάθε αυτός (ως αυτόνομη μεταβλητή δηλαδή) έχει μια δικιά του διεύθυνση, άσχετη με την διεύθυνση στην οποία δείχνει.Στην προκειμένη περίπτωση λοιπόν, εφόσον η malloc() καλείται τοπικά μέσα στην συνάρτηση κι εφόσον στην C όλα τα ορίσματα περνιούνται by-value στις συναρτήσεις, όταν τελειώσει η συνάρτηση το περιεχόμενο του δείκτη (της αυτόνομης δηλαδή μεταβλητής) δεν επιστρέφεται στον caller διότι η συνάρτηση δουλεύει με ένα "αντίγραφο" του arr.Για να δουλέψει η συνάρτηση εξαρχής πάνω τον αυθεντικό μας δείκτη, πρέπει να περαστεί η διεύθυνση του δείκτη ως όρισμα στην συνάρτηση (ο στάνταρ τρόπος δηλαδή που παρέχει η C για να προσομοιώνουμε by reference ορίσματα).Η κατανόηση αυτού ακριβώς υποθέτω πως είναι και ο βασικός λόγος για τον οποίον η άσκηση θέτει τον περιορισμό να ΜΗΝ επιστρέφει τίποτα η συνάρτηση που δημιουργεί δυναμικά τον πίνακα. Αν δεν περάσουμε τον arr by reference, τότε όχι μόνο χάνουμε τα νέα του περιεχόμενα όταν λήξει η συνάρτηση, αλλά στην προκειμένη περίπτωση έχουμε δεσμεύσει και μνήμη (μέσω της malloc() μέσα στην συνάρτηση) την οποία πλέον δεν έχουμε κανέναν τρόπο να την κάνουμε free... memory leak.Αν δεν υπήρχε αυτός ο περιορισμός, η συνάρτηση θα μπορούσε να υλοποιηθεί και κάπως έτσι... /* --------------------------------------------- */ int *arrInt20_make( void ) { int *ret = NULL, *walk = NULL; if ( NULL == (ret = malloc(20 * sizeof(int)) ) ) return NULL; for ( walk=ret; walk < ret + 20; walk++ ) *walk = 10; return ret; } και να κληθεί στην main() κάπως έτσι... ... int main( void ) { int *arr = arrInt20_make(); if ( NULL != arr ) { arrInt20_print( arr ); free( arr ); } return 0; } Η διαφορά εδώ είναι πως η arrInt20_make() δεσμεύει μνήμη και την αναθέτει μεν σε έναν τοπικό της δείκτη (τον ret) τον οποίον όμως τον επιστρέφει explicitly με το return statement, κι εμείς πλέον στην main τον αναθέτουμε στον κανονικό μας δείκτη arr.Ελπίζω να βγαίνει άκρη και να μην σας μπέρδεψα χειρότερα! EDIT: Διόρθωσα ένα typo, οπου έγραφα *ret αντί για ret όταν τον ανέθετα στη malloc(). Επεξ/σία 29 Μαρτίου 2013 από migf1 1
bird Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 ... if ( NULL == (ret = malloc(20 * sizeof(int)) ) ) return NULL; ... Επειδή το έχω ξαναδεί να το γράφεις έτσι, υπάρχει κάποιος λόγος που γράφεις: if ( NULL == (ret = malloc(20 * sizeof(int)) ) ) κι όχι if ( (ret = malloc(20 * sizeof(int)) ) == NULL ) ; 1
migf1 Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 (επεξεργασμένο) @Χάρι Πότερ: Παρακαλώ @bird: Αν έχουμε κλειστά τα warnings του compiler (που στους περισσότερους είναι η default ρύθμιση) και μας ξεφύγει κι αντί για == γράψουμε = τότε με τον τρόπο που το γράφω θα χτυπήσει error o compiler, διότι... NULL = δείκτης είναι όντως error (στις σταθερές δεν επιτρέπεται να κάνουμε ανάθεση, είναι rvalues). Αν το είχα δεξιά το NULL, ο compiler θα ανέθετε την τιμή NULL στον δείκτη χωρίς να το θεωρήσει error (γιατί αν το καλοσκεφτείς όντως δεν είναι error να αναθέτουμε την τιμή NULL σε έναν δείκτη). Μετά από εκεί ο θεός κι η ψυχή του για το τι θα κάνει το πρόγραμμά μας Επεξ/σία 29 Μαρτίου 2013 από migf1
bird Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 @bird: Αν έχουμε κλειστά τα warnings του compiler (που στους περισσότερους είναι η default ρύθμιση) και μας ξεφύγει κι αντί για == γράψουμε = τότε με τον τρόπο που το γράφω θα χτυπήσει error o compiler, διότι... NULL = δείκτης είναι όντως error (στις σταθερές δεν επιτρέπεται να κάνουμε ανάθεση, είναι rvalues). Αν το είχα δεξιά το NULL, ο compiler θα ανέθετε την τιμή NULL στον δείκτη χωρίς να το θεωρήσει error (γιατί αν το καλοσκεφτείς όντως δεν είναι error να αναθέτουμε την τιμή NULL σε έναν δείκτη). Μετά από εκεί ο θεός κι η ψυχή του για το τι θα κάνει το πρόγραμμά μας Ωραίο τρικ αυτό με τη σύγκριση μεταβλητής με σταθερά. Δεν το είχα σκεφτεί Ωστόσο αν γράφαμε κατά λάθος: if ( (ret = malloc(20 * sizeof(int)) ) = NULL ) ο compiler θα χτυπούσε παλι νομίζω γιατί αριστερά του = NULL έχουμε assignment και όλη η παρένθεση παίρνει την τιμή της εκχώρησης, όποτε όλη η παρένθεση (ret = malloc(20 * sizeof(int)) ) είναι rvalue επίσης. Όλα αυτά όπως είπα νομίζω, γιατί δεν είμαι και ειδικός
migf1 Δημοσ. 29 Μαρτίου 2013 Δημοσ. 29 Μαρτίου 2013 Ωραίο τρικ αυτό με τη σύγκριση μεταβλητής με σταθερά. Δεν το είχα σκεφτεί Ωστόσο αν γράφαμε κατά λάθος: if ( (ret = malloc(20 * sizeof(int)) ) = NULL ) ο compiler θα χτυπούσε παλι νομίζω γιατί αριστερά του = NULL έχουμε assignment και όλη η παρένθεση παίρνει την τιμή της εκχώρησης, όποτε όλη η παρένθεση (ret = malloc(20 * sizeof(int)) ) είναι rvalue επίσης. Όλα αυτά όπως είπα νομίζω, γιατί δεν είμαι και ειδικός Στο συγκεκριμένο ναι θα χτύπαγε error. Αλλά όταν έχεις συνηθίσει να βάζεις αριστερά τις σταθερές ως extra-safety measure, το γράφεις παντού έτσι (από συνήθεια αλλά και για λόγους συνέπειας). 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα