Προσπέραση scanf χωρίς καμία είσοδο  Το θέμα επιλύθηκε

...ασύγχρονα μαθήματα γλώσσας C

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό medigeek » 04 Ιουν 2011, 02:13

migf1 έγραψε:
Σε μένα δουλεύει μια χαρά πάντως βάζοντάς το στον κώδικά σου (δεν υπάρχει λόγος να μη δουλεύει). Σχετικά με το ότι δεν συνιστάται, υποτίθεται πως δεν την υποστηρίζουν όλοι οι compilers, αλλά αν όντως υπάρχουν compilers που δεν την υποστηρίζουν (δεν έχω βρει κανέναν μέχρι τώρα εγώ) τότε φταίνε αυτοί οι compilers, αφού η συνάρτηση ανήκει στις στάνταρ της γλώσσας ;)

Βρήκα το link:
* http://faq.cprogramming.com/cgi-bin/sma ... 1043284351
* http://stackoverflow.com/questions/2979 ... flushstdin
Δεν συνιστάται επειδή το c standard θεωρεί τη χρήση του για file output flushing (για γραψιμο σε αρχεία αν κατάλαβα καλά). Σύμφωνα με το C standard, είναι λάθος, ακόμη κι αν δουλεύει. :P
Κύπριος; Κόπιασε στο ubuntu-cy! ┃ Launchpad Debian Github
Οδηγός για νεοεισερχόμενους -- Αρχικές οδηγίες για αρχάριους χρήστες του Ubuntu

1 Γνώσεις Linux: Πολύ καλό ┃ Προγραμματισμού: Πολύ καλό ┃ Αγγλικών: Πολύ καλό
2 Ubuntu 12.10 quantal 3.5.0-21-generic 64bit (en_US.UTF-8, GNOME cinnamon2d), Ubuntu 3.5.0-19-generic, Windows 7
3 Intel Core2 Duo CPU E6550 2.33GHz ‖ RAM 5970 MiB ‖ MSI MS-7235
4 nVidia G73 [GeForce 7300 GT] [10de:0393] {nvidia}
5 eth0: Realtek RTL-8110SC/8169SC Gigabit Ethernet [10ec:8167] (rev 10)
Άβαταρ μέλους
medigeek
Freedom
Freedom
 
Δημοσιεύσεις: 5023
Εγγραφή: 24 Μάιος 2008, 14:49
Τοποθεσία: Σερβία/Κύπρος
Launchpad: medigeek
IRC: savvas
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό migf1 » 04 Ιουν 2011, 10:50

Ναι, τα γνωρίζω αυτά. Έγραψα ήδη πως υποτίθεται πως δεν την υποστηρίζουν όλοι οι compilers, αλλά στην πράξη δεν έχω βρει ούτε έναν που να μην την υποστηρίζει (εννοείται με stdin). Η εναλλακτική αν θες να μένεις πιστός στο στάνταρ είναι κάτι σαν κι αυτό εδώ: http://www.programmersheaven.com/mb/Can ... wont-work/ το οποίο σε human language μεταφράζεται ως "regular expressions".

Έγκειται στον προγραμματιστή να αποφασίσει αν θα πάρει το ρίσκο της μη συμβατότητας (που στην προκειμένη περίπτωση είναι ελάχιστο) έναντι του μεγάλου κόστους σε επιδόσεις όταν χρησιμοποιούμε regular expressions (ειδικά όταν χρησιμοποιείται σε όλα τα scanf(), το οποίο scanf() με τη σειρά του εκτελείται πολύ πιο αργά και χωρίς regular expressions συγκριτικά με τις εξειδικευμένες συναρτήσεις διαβάσματος strings ή απλών χαρακτήρων). Στον κώδικα που παρουσιάζεται στην αρχή του τόπικ έχει και το έξτρα μειονέκτημα πως κόβει την κάθε γραμμή του αρχείου που διαβάζει στους 63+1 χαρακτήρες. Επίσης, άσχετο, δεν κλείνει το αρχείο που έχει ανοίξει.

Ο gcc που είναι με διαφορά ο περισσότερο ported compiler σε όλες τις πλατφόρμες υποστηρίζει fflush(stdin) (όπως και η μεγάλη πλειοψηφία των σύγχρονων compilers) οπότε μένει να αποφασίσεις αν όντως αξίζει να εισαγάγεις handicap ή/και να επιβαρύνεις τον κώδικά σου :P

medigeek έγραψε:
migf1 έγραψε:
Σε μένα δουλεύει μια χαρά πάντως βάζοντάς το στον κώδικά σου (δεν υπάρχει λόγος να μη δουλεύει). Σχετικά με το ότι δεν συνιστάται, υποτίθεται πως δεν την υποστηρίζουν όλοι οι compilers, αλλά αν όντως υπάρχουν compilers που δεν την υποστηρίζουν (δεν έχω βρει κανέναν μέχρι τώρα εγώ) τότε φταίνε αυτοί οι compilers, αφού η συνάρτηση ανήκει στις στάνταρ της γλώσσας ;)

Βρήκα το link:
* http://faq.cprogramming.com/cgi-bin/sma ... 1043284351
* http://stackoverflow.com/questions/2979 ... flushstdin
Δεν συνιστάται επειδή το c standard θεωρεί τη χρήση του για file output flushing (για γραψιμο σε αρχεία αν κατάλαβα καλά). Σύμφωνα με το C standard, είναι λάθος, ακόμη κι αν δουλεύει. :P
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό Ntelispak » 04 Ιουν 2011, 12:10

Μια εναλλακτική για το πρόβλημα με το πρόγραμμα θα ήταν να χρησιμοποιηθεί
Κώδικας: Επιλογή όλων

buffer[50];
memset(buffer,'\0',50);
scanf("%s", buffer);
strcmp(buffer,"y")

Προσοχή: αν ο χαρακτήρας εισόδου είναι πάνω από σαράντα εννιά χαρακτήρες τότε υπάρχει πρόβλημα.

medigeek έγραψε:Ερώτηση: Αν χρησιμοποιήσω
Κώδικας: Επιλογή όλων
int c, var, var2;

ή
Κώδικας: Επιλογή όλων
char c, var, var2;


Το αποτέλεσμα είναι το ίδιο. Ποιο είναι το σωστό; Ή είναι τα ίδια; :?

Σε 32bit σύστημα sizeof(char)= 1byte και sizeof(int)= 4byte. Η συνάρτηση getc επιστρέφει int, αν αποθηκεύσετε το αποτέλεσμα σε char θα δημιουργηθεί data truncation, το οποίο όσον αφορά στην συγκεκριμένη συνάρτηση δεν πιστεύω να δημιουργήσει πρόβλημα. Προσωπικά, τουλάχιστον, δεν μου έχει δημιουργήσει πρόβλημα μέχρι σήμερα. Αν επιλέξετε να αποθηκεύσετε το αποτέλεσμα σε int, δεν θα υπάρξει απώλεια πληροφορίας είναι πιθανόν όμως το πρόγραμμα να γίνεται πιο αργό αν κατά την διάρκεια του χρησιμοποιείται η αποθηκευμένη τιμή πολλές φορές. Το casting κοστίζει και ειδικά το Implicit.
Οδηγείες ΧρήσηςΤutorialsΑναζήτηση
⇛ Linux: noob ┃ Προγραμματισμός: Ναι ┃ Αγγλικά: Πολύ Καλά
⇛ Xubuntu 9.04 (Jaunty Jackalope) 64bit
⇛ Intel Core2Duo E6600 (2.4GHZ 4MB) ┃ Asus P5B 965 ┃ 2x1GB DDR2 667MHZ ┃ Radeon X1600P 256MB ┃ MAudio Audiophile 192
Άβαταρ μέλους
Ntelispak
babeTUX
babeTUX
 
Δημοσιεύσεις: 36
Εγγραφή: 19 Ιουν 2009, 20:15
Τοποθεσία: Μεταμόρφωση Αττικής
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό migf1 » 04 Ιουν 2011, 13:54

Η memset() όμως, όπως επισημαίνεις ήδη, δεν εξασφαλίζει πως στο scanf() δεν θα εισαχθεί string μεγαλύτερο από το δηλωμένο μέγιστο μήκος του buffer, περίπτωση κατά την οποία το buffer δεν θα είναι null-terminated. Είναι επίσης overkill όλη αυτή η διαδικασία (και με κόστος σε επιδόσεις) για το διάβασμα και τη σύγκριση απλώς ενός χαρακτήρα.

Κάτι ακόμα τώρα, σχετικά με το διάβασμα strings γενικώς: το πρόβλημα της fgets() είναι πως διαβάζει και το '\n', που βέβαια στη περίπτωση του συγκεκριμένου προγράμματος δεν μας ενοχλεί, αλλά γενικότερα συχνά χρειάζεται να πάμε να το σβήσουμε χειροκίνητα. Επίσης, η gets() έχει έτσι κι αλλιώς πρόβλημα, αφού δεν ελέγχει καν πλήθος χαρακτήρων.

Ελπίζοντας πως θα σας φανεί χρήσιμη γενικώς και όχι μόνο για το συγκεκριμένο πρόγραμμα, σας παραθέτω μια δική μου συνάρτηση στην οποία συνδυάζω τη λειτουργικότητα των gets() και fgets(). Δηλαδή διαβάζει από το stdin μέχρι όσους χαρακτήρες της έχουμε πει ή μέχρι να βρει '\n', αλλά αντίθετα με την fgets() αυτή διαγράφει το '\n' πριν επιστρέψει.

Πρότυπο: char *s_get(char *s, size_t len);

Κώδικας:
Κώδικας: Επιλογή όλων

char *s_get(char *s, size_t len)
{
char *cp;

for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
; // for-loop with empty body */
*cp = '\0'; // null-terminate last character

return s;
}


Ανάλυση, βήμα-βήμα: http://www.gvrteam.gr/forum/viewtopic.p ... 189#p47189

Μπορείτε πολύ εύκολα να τη αλλάξετε ώστε να διαβάζει οποιοδήποτε filestrem και όχι μόνο το stdin :)
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό migf1 » 04 Ιουν 2011, 15:24

Ntelispak έγραψε:Μια εναλλακτική για το πρόβλημα με το πρόγραμμα θα ήταν να χρησιμοποιηθεί
Κώδικας: Επιλογή όλων

buffer[50];
memset(buffer,'\0',50);
scanf("%s", buffer);
strcmp(buffer,"y")

Προσοχή: αν ο χαρακτήρας εισόδου είναι πάνω από σαράντα εννιά χαρακτήρες τότε υπάρχει πρόβλημα.
[snip]

Ή ακόμα κι έτσι...
Κώδικας: Επιλογή όλων

buffer[50] = { 0 };
scanf("%s", buffer);
strcmp(buffer,"y");

αν και όπως εξηγώ και στο προηγούμενο post, και οι 2 αυτοί τρόποι είναι overkill για απλό διάβασμα χαρακτήρα!

Τουλάχιστον το...
Κώδικας: Επιλογή όλων
if ( !strcmp(buffer, "y") )

θα μπορούσε να αντικατασταθεί με το σαφώς ταχύτερο:
Κώδικας: Επιλογή όλων
if ( *buffer == 'y' ) // εναλλακτικά: if ( buffer[0] == 'y' )

αλλά και πάλι, όταν θέλουμε να διαβάσουμε απλά ένα χαρακτήρα δεν βρίσκω για ποιον λόγο να τον διαβάζουμε ως string (πόσο μάλιστα να τον συγκρίνουμε και ως string).
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό linuxs » 11 Ιουν 2011, 12:57

Πάντως το fflush ακόμη κι αν δεν δουλεύει απο την στιγμή που καθαρίζει τον buf είναι σωσ΄το σαν σκέψη. Τουλάχιστον αυτό θέλω. Το ίδιο κάνει και το κενό πρίν απο το %d.
Αν το πρόβλημά μας επιλυθεί. Επιλέγουμε το θέμα που βοήθησε στην επίλυση και πατάμε το κουμπάκι Εικόνα.
Γνώσεις ⇛ Linux: Μέτριο┃Προγραμματισμός: C┃Αγγλικά: Καλά
Λειτουργικό ⇛ Linux Ubuntu 10.4 LTS
Προδιαγραφές ⇛ Intel Pentium @T4500 2.3GHz│ 512GB VRAM│ 500 HDD│ ATI RADEON HD545v 512 MB │ Screen: 15.6''
Άβαταρ μέλους
linuxs
daemonTUX
daemonTUX
 
Δημοσιεύσεις: 1060
Εγγραφή: 02 Ιούλ 2010, 13:19
Τοποθεσία: GR
IRC: linuxs
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό migf1 » 11 Ιουν 2011, 14:59

linuxs έγραψε:Πάντως το fflush ακόμη κι αν δεν δουλεύει απο την στιγμή που καθαρίζει τον buf είναι σωσ΄το σαν σκέψη. Τουλάχιστον αυτό θέλω. Το ίδιο κάνει και το κενό πρίν απο το %d.

Δυστυχώς ανακάλυψα πως οι πρόσφατες εκδόσεις του GCC σταμάτησαν να υποστηρίζουν το fflush(stdin), οπότε αναγκαστικά χρειάζεται να διαβάζουμε ακόμα και απλούς χαρακτήρες με συναρτήσεις όπως η fgets() (ή την gcc-specific getline() που δεν υπάρχει σε άλλους compilers ή με δικιά μας συνάρτηση, όπως η s_get() που έχω ποστάρει σε άλλα νήματα) και απλά να τσεκάρουμε τον 1ο μόνο χαρακτήρα του string. Εξηγώ αναλυτικά σε αυτό εδώ το νήμα

Btw, το κενό διάστημα μέσα στο format string του scanf() δεν καθαρίζει το input buffer, απλά λέει στην scanf() να αγνοήσει τυχόν blanks (spaces, tabs)... αλλά νομίζω δεν τα σβήνει από το input buffer (δεν είμαι 100% σίγουρος). Το scanf () πρέπει να αποφεύγεται γενικώς όπου είναι δυνατόν, είναι... παλιοσυνάρτηση :lol:
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Προσπέραση scanf χωρίς καμία είσοδο

Δημοσίευσηαπό linuxs » 11 Ιουν 2011, 16:57

migf1 έγραψε:
linuxs έγραψε:Πάντως το fflush ακόμη κι αν δεν δουλεύει απο την στιγμή που καθαρίζει τον buf είναι σωσ΄το σαν σκέψη. Τουλάχιστον αυτό θέλω. Το ίδιο κάνει και το κενό πρίν απο το %d.

Δυστυχώς ανακάλυψα πως οι πρόσφατες εκδόσεις του GCC σταμάτησαν να υποστηρίζουν το fflush(stdin), οπότε αναγκαστικά χρειάζεται να διαβάζουμε ακόμα και απλούς χαρακτήρες με συναρτήσεις όπως η fgets() (ή την gcc-specific getline() που δεν υπάρχει σε άλλους compilers ή με δικιά μας συνάρτηση, όπως η s_get() που έχω ποστάρει σε άλλα νήματα) και απλά να τσεκάρουμε τον 1ο μόνο χαρακτήρα του string. Εξηγώ αναλυτικά σε αυτό εδώ το νήμα

Btw, το κενό διάστημα μέσα στο format string του scanf() δεν καθαρίζει το input buffer, απλά λέει στην scanf() να αγνοήσει τυχόν blanks (spaces, tabs)... αλλά νομίζω δεν τα σβήνει από το input buffer (δεν είμαι 100% σίγουρος). Το scanf () πρέπει να αποφεύγεται γενικώς όπου είναι δυνατόν, είναι... παλιοσυνάρτηση :lol:


Αυτό που μου έμεινε περισσότερο είναι: "παλιοσυνάρτηση" :P χαχαχαχα...

Ναι εννοείτε δεν τον καθαρίζει(φαίνεται και στο manual) κάνει αυτό ακριβώς που λές, απλά μου λειτουργεί σωστά. τουλάχιστον σε αυτό το πρόγραμμα είναι η πιο εύκολη λύση που μάλλον βρέθηκε! ;) Έτσι πιστεύω...
Αν το πρόβλημά μας επιλυθεί. Επιλέγουμε το θέμα που βοήθησε στην επίλυση και πατάμε το κουμπάκι Εικόνα.
Γνώσεις ⇛ Linux: Μέτριο┃Προγραμματισμός: C┃Αγγλικά: Καλά
Λειτουργικό ⇛ Linux Ubuntu 10.4 LTS
Προδιαγραφές ⇛ Intel Pentium @T4500 2.3GHz│ 512GB VRAM│ 500 HDD│ ATI RADEON HD545v 512 MB │ Screen: 15.6''
Άβαταρ μέλους
linuxs
daemonTUX
daemonTUX
 
Δημοσιεύσεις: 1060
Εγγραφή: 02 Ιούλ 2010, 13:19
Τοποθεσία: GR
IRC: linuxs
Εκτύπωση

Προηγούμενη

Επιστροφή στο Μαθήματα C

cron