Προς το περιεχόμενο

Διατάξεις αλφαριθμητικών στη C


capoelo

Προτεινόμενες αναρτήσεις

@defacer: μαζί γράφαμε (πάνω-κάτω τα ίδια γράψαμε μάλιστα)

 

Τελικά φίλε capoelo, θα μας εξηγήσεις τη λογική με την οποία επιλέγει το τι θα απαντήσει το πρόγραμμα;

 

Καταλαβαίνουμε ότι κάνει map το μήκος του string σε ένα index του πίνακα των string literals, και το επιστρέφει, αλλά δεν μπορώ να καταλάβω σε ποιες περιπτώσεις μπορεί να θεωρηθεί χρήσιμη η συγκεκριμένη συμπεριφορά του προγράμματος.

 

Για το goto, αν δεν το λέει το βιβλίο που διαβάζεις ότι η χρήση του ως αντικατάσταση των loops πρέπει να αποφεύγεται δια ροπάλου στον δομημένο προγραμματισμό, τότε είτε είναι (σοβαρή) παράλειψη του συγγραφέα στο συγκεκριμένο κεφάλαιο, είτε το βιβλίο δεν είναι γραμμένο από άνθρωπο με αρκετή εμπειρία, είτε είναι πάαααααρα πολύ παλιό (στις αρχές όταν είχε προωτοβγεί η C απορρόφησε αναπόφευκτα πολύ μεγάλο μέρος προγραμματιστών assembly... εκεί τα loops γίνονταν (και γίνονται) με goto).

 

Αποτελούν μια από τις αιτίες που οι δομημένες γλώσσες θεωρούνται (και είναι) μακράν πιο ευανάγνωστες και παράγουν μακράν πιο ευκολοσυντήρητα projects συγκριτικά με την assembly.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Απαντ. 63
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δεν υπονοώ ότι το παίζετε "σούπερ προγραμματιστές"(άλλωστε εκτιμώ βαθύτατα τη βοήθεια που μου παρέχετε)αλλά όπως είπε και ο defacer με πείραξε το ύφος με το οποίο αντιμετωπίστηκε η όλη κατάσταση.Τέλος πάντων,migf1 το πρόγραμμα αυτό ζητάει ερώτηση απο τον χρήστη και απαντά με βάση τον τύπο που υπάρχει στην if,δεν έχει κάποια συγκεκριμένη λογική.Και δεν χρησιμεύει πουθενά απλά το έγραψα(δεν είναι clopy-paste οπότε δεν φταίει ο συγγραφέας)για εξάσκηση.Δεν το έψαξα και πολύ γιατί ήμουν κάπως πιεσμένος χρονικά.Δεν ήξερα καν για το θέμα με τα loops και την goto και απλά μου φάνηκε μια ωραία λύση στο πρόβλημα μου.Δεν το ψάχνω και πολύ το θέμα με τη C στο ίντερνετ(γιατί είμαι πιεσμένος χρονικά-κάνω την πτυχιακή μου) και έτσι προς το παρόν βασίζομαι μόνο σε αυτά που λέει το βιβλίο(και στις γνώσεις που αποκομίζω απο το φόρουμ).Συγχωρέστε με αν κάνω παιδαριώδη λάθη αλλά...αρχάριος. :mrgreen:

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καλησπέρα capoelo,

ήθελα κάτι να επισημάνω στον τρόπο με τον οποίο διαβάζεις strings. Παρατήρησα ότι χρησιμοποίησες τη συνάρτηση gets. Μπορεί να το έκανες αυτό για να γλυτώσεις χρόνο και να επικεντρωθείς κάπου αλλού, αλλά καλό είναι να τονιστεί ότι πρέπει να αποφεύγεται. Ένα πολύ ευαίσθητο σημείο της C είναι τα strings. Μία μικρή απροσεξία μπορεί να οδηγήσει σε απρόβλεπτες καταστάσεις. Τι εννοώ:

 

Όταν διαβάζεις ένα string θα πρέπει να το διαβάζεις με scanf και μάλιστα με περιορισμό γραμμάτων, όπως φαίνεται παρακάτω:

 

char name[20];

scanf("%19s", name);

 

Με αυτόν τον τρόπο, είσαι πιο ασφαλής. Θέλεις να διαβάσεις έως και 19 χαρακτήρες, τόσους θα διαβάσεις. Μπαίνει ένα όριο δηλαδή που με την gets είναι ανύπαρκτο.

 

Αυτή η απροσεξία μπορεί να οδηγήσει σε σοβαρά προβλήματα, όπως πχ buffer overflow. Πολλοί το εκμεταλλεύονται για να πάρουν τις περισσότερες φορές τον έλεγχο του προγράμματός σου, είτε να αλλάξουν κάποιες γραμμές κώδικα, ώστε να εκτελείται κάποιος δικός τους κώδικας. Πολλές εταιρίες έχασαν πολλά λεφτά λόγω αυτής της απροσεξίας.

Τι γίνεται στο buffer overflow μπορείς να δεις εδώ.

 

EDIT: Δεν εξήγησα γιατί βάζουμε αριθμό πάντα μικρότερο κατά 1 στην scanf. Στη C, αν δηλώσουμε ένα πίνακα από χαρακτήρες, τον οποίο θα τον χρησιμοποιήσουμε ως string, πρέπει να μεριμνήσουμε ώστε η τελευταία θέση στον πίνακα που θα υφίσταται το αλφαριθμητικό να έχει τον χαρακτήρα '\0'. Η scanf το βάζει αυτόματα στο τέλος του string. Πχ, αν με την εντολή scanf("%20s", name)(η οποία είναι και η πρώτη σκέψη) διαβάσεις το "ioannis" με 7 χαρακτήρες, στην 8η θέση θα μπει το '\0'. Αν διαβάσεις το maximum που είναι 20 χαρακτήρες, τότε η scanf θα έβαζε το '\0' στην 21η θέση, η οποία δεν ανήκει στο string, και άρα το string σου δε θα κατέληγε σε '\0'. Πρόβλημα εδώ! Για αυτή την περίπτωση, την ακραία, πάντα θα βάζουμε αριθμό μικρότερο κατά 1: scanf("%19s", name).

 

Ελπίζω να κατάλαβες τι θέλω να πω. :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@capoelo: Thanks για την εξήγηση της λογικής

 

@gon1332:

 

Φοβάμαι πως ούτε αυτό που προτείνεις λύνει το πρόβλημα. Το 19 που βάζεις στην scanf() νομίζω είναι απλό suggestion, δηλαδή δεν εγγυάται πως θα διαβαστούν μονάχα τόσοι χαρακτήρες. Επειδή όμως δεν είμαι 100% σίγουρος, τσεκάρετέ το και μόνοι σας.

 

Για αυτό που είμαι 100% σίγουρος και το προτείνω πάντα είναι η χρήση της fgets(). Επίσης, στην αναθεώρηση C11 έχει προστεθεί μια ολόκληρη ομάδα νέων συναρτήσεων, με το επίθεμα _s που είναι πιο ασφαλείς (και που βασικά αποτελούσαν μακροχρόνια extensions της Microsoft, τα οποία επισημοποιήθηκαν στο C11).

 

Για όποιον ενδιαφέρεται, έχω φτιάξει μια μικρή βιβλιοθήκη ειδικά για την ασφαλή είσοδο c-strings από την κύρια είσοδο σε στάνταρ ANSI C: http://x-karagiannis.../prompt_for.php

 

Υπάρχει και σχετικό νήμα εδώ στο φόρουμ: http://www.insomnia....8D%CF%81%CE%B9/

καθώς και ειδική ιστοσελίδα τεκμηρίωσης στα ελληνικά: http://x-karagiannis.gr/prog/libs/content/misc/other/prompt_for/doc/html/

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@gon1332:

 

Φοβάμαι πως ούτε αυτό που προτείνεις λύνει το πρόβλημα. Το 19 που βάζεις στην scanf() νομίζω είναι απλό suggestion, δηλαδή δεν εγγυάται πως θα διαβαστούν μονάχα τόσοι χαρακτήρες. Επειδή όμως δεν είμαι 100% σίγουρος, τσεκάρετέ το και μόνοι σας.

 

Για αυτό που είμαι 100% σίγουρος και το προτείνω πάντα είναι η χρήση της fgets(). Επίσης, στην αναθεώρηση C11 έχει προστεθεί μια ολόκληρη ομάδα νέων συναρτήσεων, με το επίθεμα _s που είναι πιο ασφαλείς (και που βασικά αποτελούσαν μακροχρόνια extensions της Microsoft, τα οποία επισημοποιήθηκαν στο C11).

Από το πληκτρολόγιο μπορείς να υπερβείς το όριο( του 19 εδώ), αλλά με το που θα πατηθεί το enter ή κάποιο άλλο whitespace, θα μπει ένα '\0' στη θέση 20. Δηλαδή δε θα γραφτεί τίποτα σε κάποια άλλη θέση μνήμης μετά τη θέση 21. Αυτή την εντύπωση έχω.

 

Όσον αφορά τη fgets, διάβασα:

The fgets() function shall read bytes from stream into the array pointed to by s, until n-1 bytes are read, or a <newline> is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte.

...ό,τι ακριβώς κάνει και η scanf, μόνο που μεταχειρίζεται και αρχεία..

 

Δε διακρίνω κάποια διαφορά. Αν μου διαφεύγει κάτι,( που πολύ πιθανό είναι να μου διαφεύγει) θα ήταν καλό να το αναφέρετε. :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Από το πληκτρολόγιο μπορείς να υπερβείς το όριο( του 19 εδώ), αλλά με το που θα πατηθεί το enter ή κάποιο άλλο whitespace, θα μπει ένα '\0' στη θέση 20. Δηλαδή δε θα γραφτεί τίποτα σε κάποια άλλη θέση μνήμης μετά τη θέση 21. Αυτή την εντύπωση έχω.

 

Όσον αφορά τη fgets, διάβασα:

 

...ό,τι ακριβώς κάνει και η scanf, μόνο που μεταχειρίζεται και αρχεία..

 

Δε διακρίνω κάποια διαφορά. Αν μου διαφεύγει κάτι,( που πολύ πιθανό είναι να μου διαφεύγει) θα ήταν καλό να το αναφέρετε. :)

Για την ακριβή συμπεριφορά της scanf() θα πρέπει να το ψάξω στο google, γιατί το συγκεκριμένο για το οποίο συζητάμε δεν περιλαμβάνεται στην τεκμηρίωση της συνάρτησης που διαθέτω από τον gcc και την pelles-c. Αλλά δεν μπορώ τώρα γιατί ασχολούμαι με κάτι άλλο.

 

Σίγουρα πάντως η fgets() είναι τελείως διαφορετική συνάρτηση.

 

EDIT:

 

Βρήκα πρόχειρα αυτό εδώ το link: http://stackoverflow.com/questions/10886594/how-to-limit-scanf-function-in-c-to-print-error-when-input-is-too-long

που νομίζω συζητάει αυτό που θέλουμε. Δεν έχω χρόνο να το διαβάσω τώρα, αλλά σε 1-2 ώρες που θα είμαι σπίτι (βασικά ποστάρω το link περισσότερο για να το ξαναβρώ εύκολα όταν πάω σπίτι :lol:)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καταλαβαίνω τη διαφορά της fgets από την scanf. Εγώ μιλούσα για το διάβασμα string.

Η απλότητα της fgets είναι αυτό που την κάνει πιο ασφαλή στη χρήση. Η scanf έχει τη

δυνατότητα να διαβάζει formated δεδομένα και αυτό την κάνει να έχει πληθώρα επιλογών

κατά τη χρήση της. Αυτό ακριβώς την κάνει πιο επικίνδυνη: η απροσεξία και η άγνοια

του προγραμματιστή και όχι ότι μπορεί να έχει κάποιο "θέμα" στην υλοποίησή της.

 

If you haven't implemented scanf, you're not allowed to use it.

 

 

Πάντως έχω διαβάσει πως ο πλέον ασφαλής συνδυασμός για το διάβασμα string είναι ο

fgets + sscanf. Αν και τα προβλήματα που μπορεί να εμφανιστούν με την κακή χρήση της

scanf θα εμφανιστούν και σε αυτή την περίπτωση, καθώς η sscanf λειτουργεί με την ίδια

λογική. Δεν έχω ψάξει το λόγο που τον κάνει ασφαλή, γιατί δεν έχω πολύ χρόνο στη διά-

θεσή μου. Αν μπορούσε κάποιος να μου παραθέσει την γνώμη του ή κάποιο link, θα του ή-

μουν ευγνώμων.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@gon1332

 

Βασικά,όσο είμαι σε θέση να το τσεκάρω,χρησιμοποίησα την μορφή της scanf που μου πρότεινες και παρατήρησα ότι εάν συναντήσει κενό στο string,δεν εμφανίζει τίποτα απο'κει και πέρα(π.χ. αν έχω γράψει σε ένα πρόγραμμα scanf("%9s",x) και του δώσω ως είσοδο το "inso mnia foroum" θα εμφανίσει μόνο το inso).

 

Υ.Γ.Το κενό θεωρείται χαρακτήρας έτσι;Αν όχι,έγραψα μια τεράστια αρλούμπα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@gon1332

 

Βασικά,όσο είμαι σε θέση να το τσεκάρω,χρησιμοποίησα την μορφή της scanf που μου πρότεινες και παρατήρησα ότι εάν συναντήσει κενό στο string,δεν εμφανίζει τίποτα απο'κει και πέρα(π.χ. αν έχω γράψει σε ένα πρόγραμμα scanf("%9s",x) και του δώσω ως είσοδο το "inso mnia foroum" θα εμφανίσει μόνο το inso).

 

Υ.Γ.Το κενό θεωρείται χαρακτήρας έτσι;Αν όχι,έγραψα μια τεράστια αρλούμπα.

 

Το κενό είναι χαρακτήρας, αλλά είναι whitespace χαρακτήρας. Άλλοι γνωστοί σου whitespace χαρακτήρες είναι το enter, space, tab..

Για να το κάνεις να λειτουργήσει με είσοδο ολόκληρης πρότασης, θα πρέπει να γράψεις λίγο παραπάνω κώδικα. Ενδεικτικά σου παραθέτω αυτό:

>/* A way to store full input with whitespaces( the space only) in name variable */
scanf(" %15s", name);
c = getchar();
while(c != '\n'){
	strcat(name, " ");
	scanf("%15s", help);
	c = getchar();
	strcat(name, help);
}

Προσπάθησε να το καταλάβεις λίγο. Για οτιδήποτε χρειαστείς, ρωτάς.

 

Νομίζω υπάρχει και πιο άμεσος τρόπος να το κάνεις αυτό με μία μόνο scanf, αλλά δεν είμαι σίγουρος καθώς δεν την κατέχω. ;)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το κενό είναι χαρακτήρας, αλλά είναι whitespace χαρακτήρας. Άλλοι γνωστοί σου whitespace χαρακτήρες είναι το enter, space, tab..

Για να το κάνεις να λειτουργήσει με είσοδο ολόκληρης πρότασης, θα πρέπει να γράψεις λίγο παραπάνω κώδικα. Ενδεικτικά σου παραθέτω αυτό:

>/* A way to store full input with whitespaces( hre space only) in name variable */
scanf(" %15s", name);
c = getchar();
while(c != '\n'){
	strcat(cmd, " ");
	scanf("%15s", help);
	c = getchar();
	strcat(cmd, help);
}

Προσπάθησε να το καταλάβεις λίγο. Για οτιδήποτε χρειαστείς, ρωτάς.

 

Νομίζω υπάρχει και πιο άμεσος τρόπος να το κάνεις αυτό με μία μόνο scanf, αλλά δεν είμαι σίγουρος καθώς δεν την κατέχω. ;)

jsut scanf("%10[^\n]"..)

 

 

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

jsut scanf("%10[^\n]"..)

 

Αυτό εννοούσα πριν, αλλά δεν ήμουν σίγουρος :)

 

Απλά είναι καλό να καταλάβει κάποιος τι συμβαίνει και ψάχνοντας να βρει τη λύση.

Μαθαίνει πολλά πράγματα μόνος του. Η λύση του παπι είναι σωστή, απλά capoelo

προσπάθησε να καταλάβεις το πως λειτουργεί η scanf. Είναι πολύ ισχυρό εργαλείο

αν την καταλάβεις. ;)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Μόλις ήρθα σπίτι.

 

Λοιπόν...

 

καταρχήν το link είναι άσχετο με αυτό που θέλαμε να βρούμε. Έπειτα, η υπόδειξη του φίλου gon1339 για το %19s είναι σωστή, δεν έψαξα στο google, αλλά έγραψα το εξής δοκιμαστικό πρόγραμμα και λειτουργεί όπως τα περιέγραψε (δώστε του είσοδο μεγαλύτερη από 3 αλλά μικρότερη από 9 χαρακτήρες)...

 

>
#include <stdio.h>
#include <stdlib.h>

/**************************************//**
*
*****************************************
*/
int main( void )
{
char s[10] = "123......";

for (int i=0; s[i]; i++)
	putchar( s[i] );
putchar( '\n' );

scanf( "%3s", s );

for (int i=0; s[i]; i++)
	putchar( s[i] );
putchar( '\n' );

exit(0);
}

Δεν έχει δίκιο στο ότι ο συνδυασμός fgets() + sscanf() έχει τα ίδια προβλήματα με την σκέτη scanf(). Η σκέτη scanf() υποφέρει από το πρόβλημα του line-buffering (όπως υποφέρει και στον κώδικα που παρέθεσα παραπάνω). Το τι είναι το line-buffering το εξηγώ αναλυτικά στα ελληνικά στην τεκμηρίωση της prompt_for() ... έχω δώσει link σε προηγούμενο ποστ.

 

Ο τρόπος του πάπι αντιμετωπίζει το θέμα του line-buffering μέχρι το τέλος της τρέχουσας γραμμής, αλλά δεν θυμάμαι καθόλου τι κάνει με το τελικό '\n'... αν το αφήνει στο buffer της κύριας εισόδου, τότε έχουμε το ίδιο πρόβλημα (βαριέμαι τώρα να ψάχνω πάλι).

 

Ένα από τα καλύτερα άρθρα που έχω διαβάσει ποτέ για την κύρια είσοδο με C είναι αυτό εδώ: http://home.datacomm...ting_input.html (το είχα bookmarked εδώ στο σπίτι ;) ) Προτείνω ανεπιφύλακτα να το διαβάσουν όλοι ανεξαιρέτως όσοι ενδιαφέρονται να εμβαθύνουν στο θέμα.

 

Τέλος, η scanf() είναι από τις πιο πολύπλοκες και συνάμα πιο επιρρεπείς σε bugs στάνταρ συναρτήσεις της C, για αυτό και σπάνια χρησιμοποιείται σε real-life προγράμματα. Ακόμα και το επίσημο FAQ της C προτείνει να χρησιμοποιείται μονάχα αν είναι εκ των προτέρων γνωστή η δομή του input (well defined) και αναφέρεται κυρίως σε αρχεία με την fscanf().

 

EDIT:

 

Το σήκωσε η Σαράποβα.. οέο!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

 

Τέλος, η scanf() είναι από τις πιο πολύπλοκες και συνάμα πιο επιρρεπείς σε bugs στάνταρ συναρτήσεις της C, για αυτό και σπάνια χρησιμοποιείται σε real-life προγράμματα. Ακόμα και το επίσημο FAQ της C προτείνει να χρησιμοποιείται μονάχα αν είναι εκ των προτέρων γνωστή η δομή του input (well defined) και αναφέρεται κυρίως σε αρχεία με την fscanf().

 

Για εκμαθηση, μια χαρα ειναι.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δε διάβασα ακόμη το άρθρο. Απ'ότι φαντάζομαι δηλαδή, για να αποφύγουμε την

πολυπλοκότητα της scanf η καλύτερη επιλογή θα ήταν να διαβάζουμε τα πάντα

με fgets σαν strings και στη συνέχεια να μετατρέπουμε την είσοδο σε ότι

χρειαζόμαστε; Πχ με συναρτήσεις όπως strtod και strtol;

 

@migf1

Επίσης έχω διαβάσει τη βιβλιοθήκη που υλοποίησες. Έχεις κάνει πολύ καλή δουλειά,

αλλά το θέμα είναι ότι είναι πολύ high-level για μένα :P:P. Αν δεν κάτσεις να

πονοκεφαλιάσεις μέχρι να μάθεις δε λέει :P. Απλώς για κάποιον που ξεκινάει να

μαθαίνει C( ή το ο,τιδήποτε) καλό είναι να ανακαλύπτει σιγά-σιγά μέσα από πει-

ράματα τα προβλήματα και τις ευαισθησίες που εμφανίζονται μόνο με τα stadar

εργαλεία που του παρέχονται. Έτσι θα καταλάβει αργότερα τι εστί line-buffering

και ποιος είναι ο σκοπός της βιβλιοθήκης prompt_for που χρησιμοποιεί ;)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Για εκμαθηση, μια χαρα ειναι.

Διαφωνώ ρε συ, γιατί καλλιεργεί bad-habits που μετά οι περισσότεροι τα κουβαλάνε σχεδόν σε ολόκληρη την καριέρα τους. Εννοείται πως εκφράζω την προσωπική μου άποψη, κατά την οποία είναι πέρα για πέρα ατυχής η επιλογή όλων των εισαγωγικών βιβλίων να την προωθούν σαν τον στανταρ τρόπο ανάγνωσης της κύριας εισόδου.

 

ΥΓ. Το λινκ που έδωσα παραπάνω είναι παραπάνω από εξαιρετικό, αποκαλύπτοντας την πληθώρα ρίσκων που ενέχει η ανάγνωση της κύριας εισόδου γενικότερα. Btw, μόλις είδα πως ούτε ο τρόπος που πρότεινες καθαρίζει το '\n' από το buffer ;)

 

EDIT:

 

@gon1339: Συμφωνώ απόλυτα! Το γράφω κι εγώ άλλωστε, πως ιδανικά η χρήση της prompt_for() είναι για προγραμματιστές που γνωρίζουν αλλά συνειδητά την επιλέγουν, ενδεχομένως επειδή δεν έχουν το χρόνο ή την διάθεση να ασχολούνται με πολλές λεπτομέρειες. Όταν βρεις χρόνο, διάβασε οπωσδήποτε το link ;)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...