Επισκέπτης Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 void foo(char a[5][20]) { char i; for(i=0;i<5;i++) { fflush(stdin); scanf("%s",a[i]); } } int main() { char a[5][20],i; foo(a[5][20]); for(i=0;i<5;i++) { printf("%s",a[i]); } return 0; } Προσπαθώ να διαβάσω 5 strings από τον χρήστη, τα οποία αποθηκεύονται στον πίνακα a. Το πρόβλημα είναι πως το πρόγραμμα τερματίζεται και εμφανίζεται το μήνυμα "Το στοιχειο σταμάτησε να λειτουργεί". Τι έχω κάνει λάθος;
Star_Light Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 Βγαλε το fflush(stdin); και ξαναδοκιμασε.
Επισκέπτης Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 Βγαλε το fflush(stdin); και ξαναδοκιμασε. Δεν είναι αυτό το πρόβλημα. Στην αρχή δεν το είχα βάλει και γινόταν το ίδιο.
JoKo Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 Δοκίμασε κάπως έτσι: #include <stdio.h> void foo(char a[][20]) { int i; for(i = 0; i < 5; i++) { scanf("%s", &a[i][0]); fflush(stdin); } } int main() { char a[5][20]; int i; foo(a); for(i = 0; i < 5; i++) { printf("%s", a[i]); } return 0; } Το datatype του i είναι int. Το fflush(stdin) πηγαίνει καλύτερα μετά το scanf() για να κάνεις flush το stream αφού γράψει όσα χρειάζεται. Το σημαντικό στην scanf είναι να πάρει διεύθυνση ως argument (βλ. a vs. &a[0]). (Προαιρετικά) Μπορείς να βάλεις ένα printf("\n"); μετά την τελευταία printf().
Επισκέπτης Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 Ναι δούλεψε!! Φαντάζομαι το πρόβλημα ήταν στην 3η σημείωση που έκανες γιατί είχα στο μυαλό μου πως το a θα υποδηλώνει και τη διεύθυνση του a[0](όπως το b για έναν πίνακα b).Χρησημοποιώ char i για λόγους εξοικονόμησης χώρου. Αυτό δεν προκαλεί πρόβλημα αν δεν θέλω να έχω μεγάλες τιμές έτσι;Ευχαριστώ πάρα πολύ!
defacer Δημοσ. 23 Δεκεμβρίου 2013 Δημοσ. 23 Δεκεμβρίου 2013 Χρησημοποιώ char i για λόγους εξοικονόμησης χώρου. Αυτό δεν προκαλεί πρόβλημα αν δεν θέλω να έχω μεγάλες τιμές έτσι; Όχι, δεν προκαλεί πρόβλημα. Αλλά το 2014 (πλέον) και εφόσον δεν προγραμματίζεις λειτουργικό σύστημα για έξυπνα μανικετόκουμπα, το να μιλάς για εξοικονόμηση χώρου της τάξης των λίγων bytes ακούγεται (και είναι) αστείο. Νομίζω ότι θα είναι καλύτερο συνολικά για σένα το να απομακρυνθείς από τη λογική του premature optimization παρά το να κάνεις οικονομία τα bytes. 6
Star_Light Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 Για ποιο λογο πας να μπερδευτεις με 2d πινακα και δεν αποθηκευεις τα string απο τον χρηστη σε έναν πινακα απο δεικτες? Αν πχ char *planets[] = { "Νame1" , "Name2" }; Toτε το planets ειναι οντως δεικτης. Πριν δεν ηταν. Δοκιμασε να δεις με sizeof γιατι. Γενικα τους δισδιάστατους και εγω τους αποφεύγω ειδικα με σημειογραφια δεικτών γιατι ειναι λιγο ζαλάδα και ευκολο να μπερδευτεις καλη ώρα.
imitheos Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 void foo(char a[5][20]) int main() { foo(a[5][20]); } Δοκίμασε κάπως έτσι: int main() { foo(a); } Το σημαντικό στην scanf είναι να πάρει διεύθυνση ως argument (βλ. a vs. &a[0]). Ναι δούλεψε!! Φαντάζομαι το πρόβλημα ήταν στην 3η σημείωση που έκανες γιατί είχα στο μυαλό μου πως το a θα υποδηλώνει και τη διεύθυνση του a[0](όπως το b για έναν πίνακα b). Όταν έχουμε ένα πίνακα, τότε η έκφραση a ισοδυναμεί με την έκφραση &a[0] (υπάρχουν περιπτώσεις που δεν ισοδυναμεί αλλά χάριν ευκολίας ας τις αγνοήσουμε). Δηλαδή το πρόβλημά σου δεν ήταν η scanf μια και λάμβανε κανονικά την διεύθυνση που έπρεπε. Το πρόβλημά σου ήταν στη κλήση της συνάρτησης foo. Δες τι διαφορετικό έχει η κλήση του Joko από τη δική σου. Ορίζεις την συνάρτηση έτσι ώστε να δέχεται ένα πίνακα [5][20] χαρακτήρων οπότε αυτό είναι που πρέπει να της δώσεις. Ο JoKo την καλεί δίνοντας της το a ενώ εσύ της έδινες το a[5][20] το οποίο δεν είναι πίνακας αλλά ένα στοιχείο του πίνακα δηλαδή ένας απλός χαρακτήρας (και μάλιστα το συγκεκριμένο στοιχείο βρίσκεται εκτός των ορίων του πίνακα).
Star_Light Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 Εγω σημερα διαπιστωσα ενα περιεργο : #include<stdio.h> int main(void) { int arr2d[2][3]={ {1 , 2 , 3} , { 4 , 5 , 6} }; printf("%d , %d , %d , %d ", sizeof(arr2d) , sizeof(&arr2d[0][0]) , sizeof(arr2d[0][0]) , sizeof(arr2d[0])); printf("\n%p , %p , %p , %p" , arr2d , &arr2d , &arr2d[0][0] , arr2d[0]); return 0; } Ο παρπάνω κώδικας δινει αλλα αποτελεσματα σε 2 διαφορετικους μεταγλωτιστες στον gcc 4.4.5 δινει 24 , 8 , 4 , 12 και τις διευθυνσεις ενω σε code:blocks 10.05 αν το χρησιμοποιει κανεις δινει 24 , 4 , 4 , 12 =S Eπισης σε code blocks δεν αναγνωριζει τον %zu EDIT: Οπα σορρυ τωρα βρηκα οτι ο δεικτης πιάνει 4 bytes στο συστημα μου. http://stackoverflow.com/questions/6751749/size-of-a-pointer
Επισκέπτης Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 Όχι, δεν προκαλεί πρόβλημα. Αλλά το 2014 (πλέον) και εφόσον δεν προγραμματίζεις λειτουργικό σύστημα για έξυπνα μανικετόκουμπα, το να μιλάς για εξοικονόμηση χώρου της τάξης των λίγων bytes ακούγεται (και είναι) αστείο. Νομίζω ότι θα είναι καλύτερο συνολικά για σένα το να απομακρυνθείς από τη λογική του premature optimization παρά το να κάνεις οικονομία τα bytes. Το έκανα μόνο και μόνο επειδή το έθεσαν ως καλή τακτική στο τμήμα που σπουδάζω. Ευχαριστώ πολύ! Όταν έχουμε ένα πίνακα, τότε η έκφραση a ισοδυναμεί με την έκφραση &a[0] (υπάρχουν περιπτώσεις που δεν ισοδυναμεί αλλά χάριν ευκολίας ας τις αγνοήσουμε). Δηλαδή το πρόβλημά σου δεν ήταν η scanf μια και λάμβανε κανονικά την διεύθυνση που έπρεπε. Το πρόβλημά σου ήταν στη κλήση της συνάρτησης foo. Δες τι διαφορετικό έχει η κλήση του Joko από τη δική σου. Ορίζεις την συνάρτηση έτσι ώστε να δέχεται ένα πίνακα [5][20] χαρακτήρων οπότε αυτό είναι που πρέπει να της δώσεις. Ο JoKo την καλεί δίνοντας της το a ενώ εσύ της έδινες το a[5][20] το οποίο δεν είναι πίνακας αλλά ένα στοιχείο του πίνακα δηλαδή ένας απλός χαρακτήρας (και μάλιστα το συγκεκριμένο στοιχείο βρίσκεται εκτός των ορίων του πίνακα). Ναι το κατάλαβα χτες το βράδυ που δούλευα τον κώδικα. Σας ευχαριστώ πολύ όλους, με βοηθήσατε πάρα πολυ!
defacer Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 TL;DR: Η αποδοτική χρήση των πόρων του υπολογιστή είναι καλός μπούσουλας, αλλά είναι επικίνδυνη η αδιάκριτη χρήση του χωρίς να γνωρίζει κανείς τι μπαίνει στην άλλη μεριά της ζυγαριάς (ιδιαίτερα σήμερα που έχουμε τόσους πόρους που δεν είναι καν αστείο¹²). Αν σου το σύστησαν χωρίς να σου κάνουν έστω μια εισαγωγή των 10 λεπτών στο πώς και το γιατί έπραξαν κακώς. Ακολουθεί αυτή η εισαγωγή. --------------------------------------------------------------------- Το έκανα μόνο και μόνο επειδή το έθεσαν ως καλή τακτική στο τμήμα που σπουδάζω. Ευχαριστώ πολύ! Κοίτα, σαν ιδέα είναι 100% σωστή: μη σπαταλάς πόρους εφόσον δε χρειάζεται. Αυτό είναι πάντα καλό να το έχει κανείς υπόψη. Το πρόβλημα είναι πως κάποιες φορές, επειδή η σωστή εφαρμογή της συγκεκριμένη ιδέας περιέχει κρίση ("εφόσον δε χρειάζεται") αλλά η ιδέα παρουσιάζεται κάπως δογματικά, βλέπουμε το δέντρο και χάνουμε το δάσος. Σου προτείνω τέτοια πράγματα να τα βλέπεις πάντα σαν ποσοστιαία επιβάρυνση επί του συνόλου της κατανάλωσης του προγράμματος, ή αν προτιμάς να τα σκέφτεσαι με τη λογική του "critical path". Τι εννοώ. Έστω δυο functions Χ και Υ που κάνουν το ίδιο πράγμα. Η Χ είναι "καλύτερη" από την Υ επειδή καταναλώνει τους μισούς πόρους, όπου πόρος = μνήμη ή επεξεργαστική ισχύ ή ο,τι άλλο προκύψει. Όμως, και η Χ και η Υ καταναλώνουν ελάχιστους πόρους σαν απόλυτη τιμή (βασικό!) επομένως σε πρώτη φάση δε μπορεί να πει κανείς ότι η χρήση της Υ είναι ανέφικτη (προφανής ο παραλληλισμός με το συγκεκριμένο παράδειγμα που συζητάμε). Αν λοιπόν το πρόγραμμά σου είναι αυτό: printf("Hello world!\n"); call_either_X_or_Y(); τότε η διαφορά που θα δεις ανάμεσα στην κλήση της Χ ή της Υ είναι από κυριολεκτικά μηδέν (αυτό συμβαίνει στο πρόγραμμά σου: η έξτρα μνήμη που θα έτρωγε ο int είναι σε μια memory page των 4KB που ήδη έχει γίνει allocated ολόκληρη) μέχρι τόσο μικρή που αν και θεωρητικά υπάρχει στην πράξη δε μπορείς καν να τη μετρήσεις. Σ' αυτή την περίπτωση λοιπόν το ώφελος που θα αποκομίσεις είναι τόσο μικρό που δεν αξίζει καμία "παραχώρηση" εκ μέρους σου. Για τι είδους παραχώρηση μιλάω; Για παράδειγμα στον παραπάνω κώδικα ένα μέλος σου έγραψε "γιατί το κάνεις αυτό?", δείχνοντας ξεκάθαρα πως δημιουργείς σύγχυση στο μυαλό του αναγνώστη κάνοντάς το έτσι, ενώ αν κάποιος έβλεπε int απλά δε θα το σκεφτόταν. Προσωπικά η εκτίμησή μου ήταν πως το κάνεις χωρίς να ξέρεις γιατί, απλά επειδή copy paste από παράδειγμα -- όπως βλέπεις, και εγώ και σίγουρα κι άλλοι σπατάλησαν φαιά ουσία επειδή αυτό το char "δε φαίνεται σωστό". Θα πει κανείς πως η "παραχώρηση" αυτή είναι ασήμαντη, αλλά σ' αυτό έχω να απαντήσω το εξής: θεωρείς πως ο επιπλέον νοητικός φόρτος που δημιουργείς μ' αυτό τον τρόπο, ο οποίος δε μπορεί να μετρηθεί σε κιλά και που καταναλώνει ένα μέρος από τη "νοητική ισχύ" του αναγνώστη η οποία επίσης δε μπορεί να μετρηθεί σε κιλά, είναι ασήμαντος. Αλλά από την άλλη θεωρείς πως τα επιπλέον 3 bytes (που όπως είπα ούτε καν 3 δεν είναι στην πράξη) από τα MB ολόκληρα που θα δεσμεύσει το πρόγραμμα, και από τα πολλά GB μνήμης που έχει το σύστημα, είναι σημαντικά; Αυτό για μένα είναι τελείως παράλογη τοποθέτηση και δε μπορώ να την πάρω στα σοβαρά. Από την άλλη, αν το πρόγραμμά σου είναι αυτό: printf("Hello world!\n"); for(i = 0; i < 1000000000; ++i) call_either_X_or_Y(); τότε ενδεχομένως η διαφορά ανάμεσα στη Χ και την Y είναι καθοριστικής σημασίας και θα αποτελέσει το πρώτο, μη πω και μοναδικό, κριτήριο για την επιλογή σου. --------------------------------------------------------------------------------------- ¹ Το κινητό μου είναι μερικά εκατομμύρια φορές πιο γρήγορο από τον υπολογιστή όπου έμαθα προγραμματισμό και έχει εκατό χιλιάδες φορές περισσότερη RAM (είναι παλιό το καημένο, ένα καινούριο θα τα πήγαινε καλύτερα). Επίσης έχει > 100 φορές περισσότερη μνήμη και ισχύ από τον υπολογιστή όπου κάποτε έπαιζα first person shooters. Που τα έβλεπες και έτρεχαν τα σάλια. ² Σήμερα μπορείς να πας στο Πλαίσιο, να αφήσεις 500 ευρώ or so, να γυρίσεις σπίτι σου και να εγκαταστήσεις στο pc σου κάτι που έχει περισσότερη υπολογιστική ισχύ από όλους μαζί τους 500 γρηγορότερους υπερυπολογιστές στον πλανήτη πριν από 20 χρόνια. Δες τη μπλε γραμμή -- το 1993-4 είναι στο 1 TFLOP ή λίγο παραπάνω. Σήμερα μια GTX 780 βγάζει μόνη της 4 TFLOPs. Δεν περιγράφω άλλο.
Επισκέπτης Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 @defacer Όλα όσα είπες περί ασήμαντης εξοικονόμησης χώρου μας τα εξήγησαν και με το παραπάνω, οπότε δεν πρόκειται για copy-paste. Αυτό που δεν μας είπαν είναι ότι κάποιος μπορεί να μπερδευτεί(και δεν το περίμενα να πώ την αλήθεια) οπότε μάλλον πρέπει να αναθεωρήσω.
defacer Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 @defacer Όλα όσα είπες περί ασήμαντης εξοικονόμησης χώρου μας τα εξήγησαν και με το παραπάνω, οπότε δεν πρόκειται για copy-paste. Αυτό που δεν μας είπαν είναι ότι κάποιος μπορεί να μπερδευτεί(και δεν το περίμενα να πώ την αλήθεια) οπότε μάλλον πρέπει να αναθεωρήσω. Η ουσία είναι πως τράβηξε την προσοχή μας. Χαρμόσυνο το ότι σας τα εξήγησαν όμως! Τώρα όσο για το δεν το περίμενες να μπερδευτεί κανείς, γράψε ένα πρόγραμμα όχι των 50 γραμμών, άστο στην άκρη και ξαναδιάβασέ το εσύ ο ίδιος που το έγραψες μετά από ένα χρόνο. Θα μείνεις έκπληκτος με το πόσο εύκολα μπορεί να σκαλώσει κανείς.
imitheos Δημοσ. 24 Δεκεμβρίου 2013 Δημοσ. 24 Δεκεμβρίου 2013 Χρησημοποιώ char i για λόγους εξοικονόμησης χώρου. Αυτό δεν προκαλεί πρόβλημα αν δεν θέλω να έχω μεγάλες τιμές έτσι; Ο defacer σου έδωσε μια πολύ σωστή εξήγηση για ποιο λόγο δεν πρέπει να ασχολείσαι με τέτοια θέματα στους σημερινούς υπολογιστές. Πέρα από αυτό ίσως αξίζει εγκυκλοπαιδικά να αναφέρουμε ότι ακόμη και να είχε νόημα η παραπάνω "εξοικονόμηση" δεν σου εγγυάται κανείς ότι θα συμβεί. Ένας compiler είναι ελεύθερος να αλλάξει τον κώδικά σου με οτιδήποτε κώδικα θέλει αρκεί το τελικό αποτέλεσμα να είναι ίδιο. Και πράγματι, ο gcc σε x86 μέχρι και το στάδιο βελτιστοποίησης O1 χρησιμοποιεί char όπως δήλωσες αλλά από το στάδιο O2 παράγει κώδικα σαν να είχες δηλώσει int (γιατί η προσπέλαση του είναι πιο γρήγορη).
JoKo Δημοσ. 25 Δεκεμβρίου 2013 Δημοσ. 25 Δεκεμβρίου 2013 Πολύ σωστό το σχόλιο του imitheou εδώ, ξέχασα να σχολιάσω τη χρήση του πίνακα στην κλήση της foo(). Όσον αφορά το data type του i, μπορείς να χρησιμοποιήσεις το data type uint8_t (unsigned integer των 8 bits) για να υποδηλώσεις τη χρήση του στον κώδικα. Πάντως οι compilers μπορούν να γίνουν αρκετά aggressive ό,τι data type και αν βάλεις, οπότε εγώ τουλάχιστον προτιμώ να καταλαβαίνω εύκολα τη χρήση των μεταβλητών παρά να κάνω premature optimization.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα