antbyron Δημοσ. 8 Ιανουαρίου 2014 Δημοσ. 8 Ιανουαρίου 2014 Έχω αυτόν εδώ τον πολύ απλό κώδικα #include <stdio.h> #include <stdlib.h> main() { char title[61]; char artist[61]; char type; int tracks; float price; int album; printf("welcome to the cd database.\n\n"); /* * prompting the user to give the details of the cd */ printf("Please enter the details of the cd...\n"); printf("Title:"); scanf("%s",title); printf("Artist:"); fflush(stdin); scanf("%s",artist); printf("Number of tracks:"); fflush(stdin); scanf("%d",&tracks); printf("Album or single(a for album, s for single):"); //getchar(); fflush(stdin); /*προσπαθώ να αδειάσω το buffer αλλά δεν λειτουργεί*/ scanf("%c", &type); album = type == 'a'; /* if they didn't enter 'a' or 's' it assums 's'*/ printf("Price:"); fflush(stdin); scanf("%f",&price); printf("\nThe cd details you entered are:\n"); printf("=================================\n"); printf("Title:%s\n", title); printf("Artist:%s\n", artist); printf("Number of tracks:%d\n",tracks); if (album) printf("Album\n"); else printf("Single\n"); printf("Price:%.2f\n",price); printf("===================================\n"); } Το πρόβλημα μου είναι όταν προσπαθώ να πάρω δεδομένα τύπου char από τον χρήστη. Χρησιμοποιώ το fflush(stdin); για να καθαρίζω το buffer. Ενώ λειτουργεί σε δεδομένα τύπου int και string, δεν λειτουργεί για δεδομένα τύπου char. Η ερώτηση μου είναι λοιπόν, γιατί δεν λειτουργεί;
migf1 Δημοσ. 8 Ιανουαρίου 2014 Δημοσ. 8 Ιανουαρίου 2014 Η fflush(stdin) δουλεύει μόνο σε Windows, κι αυτό καταχρηστικά σύμφωνα με τα στάνταρ της γλώσσας. Αντί για scanf(); fflush(stdin); μια quick & dirty λύση είναι να χρησιμοποιήσεις: fgets(); sscanf(); περνώντας ως 1ο όρισμα ένα cstring που είναι πολύ μεγαλύτερο σε μήκος από ότι περιμένεις να σου δώσει ο χρήστης (π..χ. 512 chars). Αν στο κάνει overflow, πάλι θα έχεις πρόβλημα με το line-buffering, αλλά πρέπει να το κάνει επίτηδες ο χρήστης. Αν θες να είσαι καλυμμένος 100% μπορείς να φτιάξεις μια δικιά σου flush_stdin() ρουτίνα και να την καλείς αν η fgets() γίνει overflow (ή μπορείς να φτιάξεις μια δικά σου ρουτίνα τύπου fgets() μόνο για την κύρια είσοδο). 1
antbyron Δημοσ. 8 Ιανουαρίου 2014 Μέλος Δημοσ. 8 Ιανουαρίου 2014 Η fflush(stdin) δουλεύει μόνο σε Windows, κι αυτό καταχρηστικά σύμφωνα με τα στάνταρ της γλώσσας. Αντί για scanf(); fflush(stdin); μια quick & dirty λύση είναι να χρησιμοποιήσεις: fgets(); sscanf(); περνώντας ως 1ο όρισμα ένα cstring που είναι πολύ μεγαλύτερο σε μήκος από ότι περιμένεις να σου δώσει ο χρήστης (π..χ. 512 chars). Αν στο κάνει overflow, πάλι θα έχεις πρόβλημα με το line-buffering, αλλά πρέπει να το κάνει επίτηδες ο χρήστης. Αν θες να είσαι καλυμμένος 100% μπορείς να φτιάξεις μια δικιά σου flush_stdin() ρουτίνα και να την καλείς αν η fgets() γίνει overflow (ή μπορείς να φτιάξεις μια δικά σου ρουτίνα τύπου fgets() μόνο για την κύρια είσοδο). Ευχαριστω, οπότε επειδή δουλεύω σε linux δεν λειτουργεί η fflush(stdin). Προς το παρόν λύνω το πρόβλημα μου με getchar().
migf1 Δημοσ. 8 Ιανουαρίου 2014 Δημοσ. 8 Ιανουαρίου 2014 Με την getchar() θα έχεις πάλι πρόβλημα αν επιχειρήσεις να διαβάσεις κι άλλο char (θα πάρει ως input το '\n' του προηγούμενου input). Δες ένα all-around παράδειγμα που δουλεύει πάντα (ελπίζω)... #include <stdio.h> #define MAX_INPUT (80+1) /* ------------------------------------------------ * Read up to ssize-1 chars from stdin into s, or until EOF is encountered, * and nul-terminate s (replacing the trailing '\n' if any). * Return either s unchanged if ssize is passed as 0, * or NULL if s is passed as NULL. */ char *my_gets( char *s, size_t ssize ) { size_t i = 0; int c = '\0'; /* sanity checks */ if ( !s || ssize < 1 ) { return s; } /* read chars from stdin */ for (i=0; i < ssize-1 && '\n' != (c=getc(stdin)) && EOF != c; i++) { s[i] = c; } /* flush overflowed chars, if any */ if ( s[i] != '\n' ) { /* EOF or ssize reached without '\n' */ while (getchar() != '\n') /* ... flush remaining chars */ ; /* ... ... empty-loop body */ } s[i] = '\0'; return s; } /* ------------------------------------------------ */ int main( void ) { char input[ MAX_INPUT ] = '\0'; int n, m, c; ... my_gets(input, MAX_INPUT); sscanf(input, "%d", &n); ... my_gets(input, MAX_INPUT); sscanf(input, "%c", &c); ... my_gets(input, MAX_INPUT); sscanf(input, "%d", &m); ... return 0; }
gon1332 Δημοσ. 8 Ιανουαρίου 2014 Δημοσ. 8 Ιανουαρίου 2014 Άκου τι θα κάνεις. Πρώτον δε θα χρειαστείς τα fflush. Το πρόβλημά σου είναι εδώ: printf("Number of tracks:"); scanf("%d",&tracks); printf("Album or single(a for album, s for single):"); scanf("%c", &type); Μία ενδεικτική εκτέλεση για αυτό το snipet είναι αυτή: Number of tracks:23<enter> Album or single(a for album, s for single):Price: Όπως σωστά υπέθεσες ο buffer δεν αδειάζει κι έτσι το newline σου χαλάει την εκτέλεση. Μία απλή λύση είναι να το "πετάξεις" με τη δυνατότητα που σου προσφέρει η scanf. Μπορείς να γράψεις: scanf(" %c", &type); Αν βάλεις ένα κενό πριν τον identifier του χαρακτήρα, τότε θα φορμάρεις την είσοδο του χρήστη, έτσι ώστε να απορρίπτει το πρώτο whitespace που θα διαβάσει. Δοκίμασέ το!
migf1 Δημοσ. 9 Ιανουαρίου 2014 Δημοσ. 9 Ιανουαρίου 2014 ... Δοκίμασέ το! Και πάλι θα έχει πρόβλημα αν μετά διαβάσει χωρίς scanf, π.χ... int main(void) { int n, m; char c, s[80+1] = {'\0'}; printf("Number of tracks:"); scanf("%d",&n); printf("Album or single(a for album, s for single):"); scanf(" %c", &c); printf("Title: "); fgets(s, 80+1,stdin); puts(s); printf("Price:"); scanf("%d", &m); getchar(); return 0; } H fgets() παραπάνω (που τη χρησιμοποίησα π.χ. επειδή θέλω να διαβάσω τον τίτλο μονοκόμματα με όλα τα κενά που περιέχει) διαβάζει μόνο το '\n' που άφησε στην stdin η scanf(" %c", &c); ΥΓ. Μπορείς και με την scanf() να διαβάζεις μονκόματα με όλα τα κενά, αλλά πρέπει να ανατρέξεις στο manual (τουλάχιστον εγώ, για να θυμηθώ την regex-like σύνταξη της).
giorgos147 Δημοσ. 12 Ιανουαρίου 2014 Δημοσ. 12 Ιανουαρίου 2014 Θα κάνω μία χαζή ερώτηση για την C. Αρκετά χαζή, αλλά δε μπορώ να το καταλάβω με τίποτα, ό,τι και να διάβασα, ακόμη και βιντεάκια που είδα. Τι παίζει με τους δείκτες και τον τελεστή &; Ποια η λογική τους; Π.χ. όταν θέλω να διαβάσω κάτι από το πληκτρολόγιο, γιατί πρέπει να βάλω το & μπροστά από τη μεταβλητή μου; Μάλλον τα έχω μπερδέψει, αλλά προσωπικά έχω καταλάβει ότι ένας δείκτης δείχνει τη θέση μνήμης στην οποία αντιστοιχεί μία μεταβλητή. Αν ισχύει αυτό, δηλαδή ότι δείχνει σε μία θέση μνήμης, γιατί να το χρησιμοποιήσω; Και ειδικότερα, πώς συνδυάζονται δείκτης και τελεστής &; Όσο πιο απλά γίνεται παιδιά. Ευχαριστώ και συγγνώμη που χρησιμοποίησα το παρόν θέμα!!
Star_Light Δημοσ. 12 Ιανουαρίου 2014 Δημοσ. 12 Ιανουαρίου 2014 Θα κάνω μία χαζή ερώτηση για την C. Αρκετά χαζή, αλλά δε μπορώ να το καταλάβω με τίποτα, ό,τι και να διάβασα, ακόμη και βιντεάκια που είδα. Τι παίζει με τους δείκτες και τον τελεστή &; Ποια η λογική τους; Π.χ. όταν θέλω να διαβάσω κάτι από το πληκτρολόγιο, γιατί πρέπει να βάλω το & μπροστά από τη μεταβλητή μου; Μάλλον τα έχω μπερδέψει, αλλά προσωπικά έχω καταλάβει ότι ένας δείκτης δείχνει τη θέση μνήμης στην οποία αντιστοιχεί μία μεταβλητή. Όσο πιο απλά γίνεται παιδιά. Ευχαριστώ και συγγνώμη που χρησιμοποίησα το παρόν θέμα!! int x = 2; int *p = &x; Η p ειναι τώρα δεικτης και δειχνει στην x. Με *p μπορεις να τροποιήσεις την τιμή της x επειδη το *p ειναι ισοδυναμο με το x ενω το p με το &x. Αν *p = 3; τοτε η τιμή της x τωρα θα ειναι 3 και οχι 2. Ο τελεστής & δινει την διεύθυνση μνήμης για μια μεταβλητή. Με την scanf διαβάζεις εναν ακεραιο απο το πληκτρολογιο και τον βάζεις στην διεύθυνση που πρέπει , έχεις δεσμευσει πιο πριν μια κατανομασμένη θέση μνήμης πχ x και λες στην scanf να βάλει την τιμή που διάβασε σε αυτη την τοποθεσια μνήμης. Μετα μπορεις να εκτυπώσεις αυτη την τιμή με μια άμεση αναφορά (το identifier της μεταβλητής που ειναι το ονομά της το x δηλαδη) ή με μια έμμεση αναφορά το *p. Μπορεις να μας γράψεις την έκφραση που δίνει την διεύθυνση του ιδιου του δείκτη?
giorgos147 Δημοσ. 12 Ιανουαρίου 2014 Δημοσ. 12 Ιανουαρίου 2014 Οπ, σαν κάτι να έγινε. Χίλια ευχαριστώ Star_Light. Νιώθω ότι το ψιλοξεμπέρδεψα. 1
antbyron Δημοσ. 30 Ιανουαρίου 2014 Μέλος Δημοσ. 30 Ιανουαρίου 2014 Έχω αυτόν τον κώδικα main() { char name[51]; char name2[51]; int i=0; printf("Please enter your name: "); scanf("%s",name); while(name[i]!= '\0') { name2[i++] = name[i]; } puts(name2); printf("the name is %s",name2); } προπαθώ να αντιγράψω το ονομα που θα μου δινει ο χρήστης σε ενναν πινακα και μετα απο τον πινακα να το αντιγρψω σε ενα δευτερο πινακα. το προβλημα βρισκεται οταν βαζω το ονομα στον πρωτο πινακα και τον μεταφερω στον δευτεορ πινακα τοτε εξαγωντας το ονομα απο τον δευτερο, παρακαμπτει τον πρωτο χαρακηρα. Τι δεν λειτουργει σωστα; Επισης υπάρχει και αυτή η λύση main() { char name[51]; char name2[51]; int i=0; printf("Please enter your name: "); scanf("%s",name); while(name[i]!= '\0') { name2[i] = name[i]; i+=1; } puts(name2); printf("the name is %s",name2); } που ομως πεταει σκουπιδια μετα το ονομα
migf1 Δημοσ. 30 Ιανουαρίου 2014 Δημοσ. 30 Ιανουαρίου 2014 (επεξεργασμένο) Καταρχήν στη C δεν υπάρχει δήλωση main() έτσι όπως την έχεις. Για το θέμα με τα ονόματα, δοκίμασε... int main( void ) { char name[51] = {'\0'}; char name2[51] = {'\0'}; printf("Please enter your name: "); scanf("%50s", name); for (int =0; i < 50 && name[i] != '\n'; i++) { name2[i] = name[i]; } puts(name); puts(name2); return 0; } EDIT: http://www.insomnia.gr/topic/513504-%CE%B1%CE%BA%CE%B1%CF%84%CE%B1%CE%BD%CF%8C%CE%B7%CF%84%CE%B7-%CF%83%CF%85%CE%BC%CF%80%CE%B5%CF%81%CE%B9%CF%86%CE%BF%CF%81%CE%AC-c/?do=findComment&comment=52952130 Επεξ/σία 31 Ιανουαρίου 2014 από migf1
gon1332 Δημοσ. 30 Ιανουαρίου 2014 Δημοσ. 30 Ιανουαρίου 2014 Έχω αυτόν τον κώδικα main() { char name[51]; char name2[51]; int i=0; printf("Please enter your name: "); scanf("%s",name); while(name[i]!= '\0') { name2[i++] = name[i]; } puts(name2); printf("the name is %s",name2); } προπαθώ να αντιγράψω το ονομα που θα μου δινει ο χρήστης σε ενναν πινακα και μετα απο τον πινακα να το αντιγρψω σε ενα δευτερο πινακα. το προβλημα βρισκεται οταν βαζω το ονομα στον πρωτο πινακα και τον μεταφερω στον δευτεορ πινακα τοτε εξαγωντας το ονομα απο τον δευτερο, παρακαμπτει τον πρωτο χαρακηρα. Τι δεν λειτουργει σωστα; Σε αυτή τη περίπτωση η συμπεριφορά αυτή οφείλεται στο γεγονός ότι αυξάνεις το i πριν κάνεις την ανάθεση. Με την γραμμή, name2[i++] = name[i]; γίνονται οι εξής κινήσεις με αυτή τη σειρά: Αύξηση κατά 1 του i και φόρτωση του name[i_new] στο name2[i_new]. Έτσι αν name = "john", τότε στην πρώτη επανάληψη name2[1] = name[1] = 'j'. Αυτό το πρόβλημα λύνεται με τον τρόπο που έκανες στο δέυτερο πρόγραμμα. Με την αλλαγή του i μετά την ανάθεση. Τώρα το γεγονός ότι στο πρώτο πρόγραμμα μετά τη δουλειά που έκανες, το νέο string δεν ακολουθούνταν από "σκουπίδια" είναι κάτι το τυχαίο. Τα strings στη C είναι NULL terminated, πράγμα που σημαίνει ότι στο τέλος της χρήσιμης πληροφορίας μπαίνει ένας χαρακτήρας '\0'. Μετά από αυτόν ακολουθούν "σκουπίδια". Γι' αυτό και στην επανάληψη ψάχνεις τους χαρακτήρες του string μέχρι να συναντήσεις '\0'. Στην περίπτωσή μας τώρα εσύ θέλεις να τερματίσεις την επανάληψη μόλις διαβάσεις τον NULL character. Στο σημείο όμως στο οποίο κάνεις αύξηση του i, στο name2 αντιγράφεται στο τέλος και ο χαρακτήρας NULL από το name. Στην ουσία κοιτάς ένα σημείο μπροστά. Στο δεύτερο κώδικά σου κάτι τέτοιο δε συμβαίνει. Οπότε εύκολα καταλαβαίνεις πως η επανάληψη τερματίζεται πριν αντιγράψεις και το NULL στο name2. Μετά όταν κάνεις την εκτύπωση του name2, εκτυπώνεται το string μέχρι να βρεθεί NULL, το οποίο βρίσκεται στο 51ο χαρακτήρα. Άρα διαβάζονται και όλα τα σκουπίδια, μέχρι εκείνο το σημείο. Υπάρχουν δύο λύσεις: αρχικοποιείς τα strings σου με NULL, όπως σου έδειξε ο migf1, είτε όταν εξέλθεις της επανάληψης θέτεις name2 = '\0'. Για το τελευταίο πρέπει να έχεις το νου σου συνέχεια, οπότε προτίμησε το πρώτο για να έχεις το κεφάλι σου ήσυχο.
imitheos Δημοσ. 30 Ιανουαρίου 2014 Δημοσ. 30 Ιανουαρίου 2014 Σε αυτή τη περίπτωση η συμπεριφορά αυτή οφείλεται στο γεγονός ότι αυξάνεις το i πριν κάνεις την ανάθεση. Με την γραμμή, name2[i++] = name[i]; γίνονται οι εξής κινήσεις με αυτή τη σειρά: Αύξηση κατά 1 του i και φόρτωση του name[i_new] στο name2[i_new].Έτσι αν name = "john", τότε στην πρώτη επανάληψη name2[1] = name[1] = 'j'. Αυτό το πρόβλημα λύνεται με τον τρόπο που έκανες στο δέυτερο πρόγραμμα. Με την αλλαγή του i μετά την ανάθεση. Γιατί αυξάνεται κατά ένα και μετά έχουμε παντού τον i_new ? Τον postfix τελεστή χρησιμοποίησε όχι τον prefix. Επίσης τον έχει στην αριστερή πλευρά της ανάθεσης οπότε μια λογική ερμηνεία της λειτουργίας θα ήταν ότι διαβάζεται το περιεχόμενο της μνήμης name, έπειτα αυτό τοποθετείται στην διεύθυνση name2 και μετά λόγω postfix αυξάνεται το i, δηλαδή αυτό που ήθελε ο OP. Το λογικό όμως δεν ισχύει εδώ γιατί πέφτουμε σε αόριστη συμπεριφορά και μπορεί το αποτέλεσμα να είναι ό,τι θέλει ο compiler. Ίσως να μη το θυμάμαι καλά αλλά νομίζω δεν μπορείς να αλλάζεις την μεταβλητή στις δύο πλευρές της ανάθεσης (παρόμοιο δηλαδή με το i = i++ + i++)
gon1332 Δημοσ. 30 Ιανουαρίου 2014 Δημοσ. 30 Ιανουαρίου 2014 Το λογικό όμως δεν ισχύει εδώ γιατί πέφτουμε σε αόριστη συμπεριφορά και μπορεί το αποτέλεσμα να είναι ό,τι θέλει ο compiler. Ίσως να μη το θυμάμαι καλά αλλά νομίζω δεν μπορείς να αλλάζεις την μεταβλητή στις δύο πλευρές της ανάθεσης (παρόμοιο δηλαδή με το i = i++ + i++) Σωστά! Ο compiler παραπονιέται για το postfix increment του i στο name2[i++]: warning: operation on ‘i’ may be undefined [-Wsequence-point] οπότε τα αποτελέσματα δεν είναι αυτά που θέλουμε.
migf1 Δημοσ. 31 Ιανουαρίου 2014 Δημοσ. 31 Ιανουαρίου 2014 Να σημειώσω κι εγώ πως έχει λάθος το σνίπετ που πόσταρα παραπάνω... εκ παραδρομής έχω γράψει '\n' αντί για το σωστό '\0' στη συνθήκη του loop. (έχω επίσης διπλο-τριπλο-ελέγχους οι οποίοι στη συγκεκριμένη ερώτηση δε χρειάζονται... αλλά μπορεί να αποδειχτούν χρήσιμοι αν ο κώδικας γίνει συνάρτηση κάποια στιγμή)
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα