Calculator γραμμένο σε C

...του ubuntu και έργων ΕΛ/ΛΑΚ (Έργα-Οδηγοί-Προτάσεις)

Συντονιστής: konnn

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό UnKnown96 » 20 Ιουν 2011, 18:17

Έκανα ορισμένες αλαγές στην V5...
Στα παντζούρια όλα καλά, μόνο το Bug της διαίρεσης έμεινε να διορθώσω που θα βγάλω στην τελική έκδοση με ορισμένα Tweaks που σκέφτομαι να κάνω.
Τώρα, για το linux δυστιχώς πρέπει να βρω αντικαταστάτη για το fflush(stdin). Δεν καταλαβένω την GNU καθόλου.
Άβαταρ μέλους
UnKnown96
dudeTUX
dudeTUX
 
Δημοσιεύσεις: 370
Εγγραφή: 08 Ιουν 2010, 15:23
Τοποθεσία: Ρόδος
Εκτύπωση

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό migf1 » 20 Ιουν 2011, 19:55

UnKnown96 έγραψε:Έκανα ορισμένες αλαγές στην V5...
Στα παντζούρια όλα καλά, μόνο το Bug της διαίρεσης έμεινε να διορθώσω που θα βγάλω στην τελική έκδοση με ορισμένα Tweaks που σκέφτομαι να κάνω.

Ούτε στα Windows είναι τελείως ΟΚ, π.χ. δεν αντιδράει σωστά αν στις πράξεις αντί για αριθμό βάλεις γράμματα, έστω και κατά λάθος.

έγραψε:Τώρα, για το linux δυστιχώς πρέπει να βρω αντικαταστάτη για το fflush(stdin). Δεν καταλαβένω την GNU καθόλου.

Δεν υπάρχει αντικαταστάτης. Μπορείς όμως να απενεργοποιήσεις το line-buffering μέσα στο πρόγραμμά σου (και να το ενεργοποιείς ξανά πριν τερματίσεις) γυρίζοντας το tty του τερματικού σου σε raw mode, με συναρτήσεις της βιβλιοθήκης <termios.h> (και πιο συγκεκριμένα την: tcsetattr() ).

Αυτό το link θα σε βοηθήσει, σημείωσε όμως πως αυτός ο κώδικάς τρέχει μόνο σε περιβάλλον POSIX και όχι σε Windows.

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

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό UnKnown96 » 20 Ιουν 2011, 20:09

Είναι νωρίς για GUI ακόμα. Τουλάχιστον για εμένα.
Αυτό που λες στα Windows όντως γίνεται. Αυτό και η διαίρεση.
Αλλά αυτό με το fflush στο linux... Είναι δυνατόν να είμαι αναγκασμένος να περάσω του Χριστού τα βάσσανα επείδη η GNU χάζεψε; Εννοείτε πως όχι, σιγά μην αρχίσω να διαβάζω επειδή δεν γουστάρουν το fflush...

Off topic:
Ένα κακό πράγμα στο Linux είναι πως κάτι λεπτομέριες σαν και αυτές κάνουν τους άλλους να το κοιτάνε με μισό μάτι και να πιστεύουν πως είναι καλό για μάθεις ορισμένα πράγματα και για hobby και τίποτα παραπάνω, έτσι τουλάχιστον έχω καταλάβει από συζητήσεις με άλλους που ασχολούντε χρόνια με την πληροφορική και έχουν ορισμένη γνώση για το Linux.
Άβαταρ μέλους
UnKnown96
dudeTUX
dudeTUX
 
Δημοσιεύσεις: 370
Εγγραφή: 08 Ιουν 2010, 15:23
Τοποθεσία: Ρόδος
Εκτύπωση

Re: Calculator γραμμένο σε C

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

Πάντως απορώ γιατί παιδεύεσαι και γιατί επιμένεις στη χρήση της scanf() και δεν κάνεις το πρόγραμμά σου portabe παντού, διαβάζοντας το input ως string και μετατρέποντάς το μετά σε πραγματικό αριθμό, ακέραιο, ή ότι άλλο χρειάζεσαι.

Σου έχω αναλύσει τον αλγόριθμο, σου έχω δώσει και κώδικα που το κάνει.

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

Re: Calculator γραμμένο σε C

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

Το fflush() δεν έχει να κάνει αποκλειστικά με τον compiler. Είναι συνδυασμός compiler και της υλοποίησης του τερματικού (κονσόλας) στις διάφορες πλατφόρμες.

UnKnown96 έγραψε:Είναι νωρίς για GUI ακόμα. Τουλάχιστον για εμένα.
Αυτό που λες στα Windows όντως γίνεται. Αυτό και η διαίρεση.
Αλλά αυτό με το fflush στο linux... Είναι δυνατόν να είμαι αναγκασμένος να περάσω του Χριστού τα βάσσανα επείδη η GNU χάζεψε; Εννοείτε πως όχι, σιγά μην αρχίσω να διαβάζω επειδή δεν γουστάρουν το fflush...

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

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό UnKnown96 » 20 Ιουν 2011, 20:13

Δεν καταλαβαίνω τον κώδικα σου, μου φαίνονται κινέζικα και ότι έχω κάνει από ιδέες σου το κάνω με δικό μου κώδικα.
Ιδικά τα pointers και τα arrays παρόλο που διάβασα πολύ για αυτά...

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

Σου έχω αναλύσει τον αλγόριθμο, σου έχω δώσει και κώδικα που το κάνει.

Και τα GUI σαν string το διαβάζουν το οποιοδήποτε input.
Τελευταία επεξεργασία από UnKnown96 και 22 Ιουν 2011, 18:13, έχει επεξεργασθεί 1 φορά/ες συνολικά
Άβαταρ μέλους
UnKnown96
dudeTUX
dudeTUX
 
Δημοσιεύσεις: 370
Εγγραφή: 08 Ιουν 2010, 15:23
Τοποθεσία: Ρόδος
Εκτύπωση

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό UnKnown96 » 20 Ιουν 2011, 20:19

migf1 έγραψε:Το fflush() δεν έχει να κάνει αποκλειστικά με τον compiler. Είναι συνδυασμός compiler και της υλοποίησης του τερματικού (κονσόλας) στις διάφορες πλατφόρμες.

UnKnown96 έγραψε:Είναι νωρίς για GUI ακόμα. Τουλάχιστον για εμένα.
Αυτό που λες στα Windows όντως γίνεται. Αυτό και η διαίρεση.
Αλλά αυτό με το fflush στο linux... Είναι δυνατόν να είμαι αναγκασμένος να περάσω του Χριστού τα βάσσανα επείδη η GNU χάζεψε; Εννοείτε πως όχι, σιγά μην αρχίσω να διαβάζω επειδή δεν γουστάρουν το fflush...

Off topic:
Ένα κακό πράγμα στο Linux είναι πως κάτι λεπτομέριες σαν και αυτές κάνουν τους άλλους να το κοιτάνε με μισό μάτι και να πιστεύουν πως είναι καλό για μάθεις ορισμένα πράγματα και για hobby και τίποτα παραπάνω, έτσι τουλάχιστον έχω καταλάβει από συζητήσεις με άλλους που ασχολούντε χρόνια με την πληροφορική και έχουν ορισμένη γνώση για το Linux.


Όπως και να είναι το scanf με το fflush μαζί κάνουν καλή δουλειά και είναι στα μέτρα μου...
Αν κάτσω να κάνω τέτοιες μετατροπές τώρα, δεν θα καταφέρω τίποτα...
Άβαταρ μέλους
UnKnown96
dudeTUX
dudeTUX
 
Δημοσιεύσεις: 370
Εγγραφή: 08 Ιουν 2010, 15:23
Τοποθεσία: Ρόδος
Εκτύπωση

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό migf1 » 20 Ιουν 2011, 20:47

Δεν είναι τόσο τραγικό πάντως, ούτε χρειάζεται να εμβαθύνεις σε pointers κλπ απλά για να διαβάσεις ένα string. Μπορείς να χρησιμοποιήσεις την συνάρτηση: s_get() ατόφια όπως σου τη δίνω στον κώδικά μου για να διαβάζεις όποιο string θέλεις.

Π.χ. ορίζεις έναν string π.χ. line ως πίνακα από 256 char:
Κώδικας: Επιλογή όλων

char line[255+1];


Ξεκίνα το απλά, όπως το έκανες και με το scanf(), όπου θα διαβάζεις έναν αριθμό τη φορά (κι όχι πολλούς μαζί σε μια γραμμή). Μόλις τον διαβάσεις με την:
Κώδικας: Επιλογή όλων
s_get( line, 256 );

μπορείς να χρησιμοποιήσεις μια από τις στάνταρ συναρτήσεις: strtod() ή atof() (εναλλακτικά μπορείς να δεις τα man pages τους στο linux σου) για να το μετατρέψεις σε αριθμό.

Η atof() είναι πιο απλή από την strtod() από την άποψη πως δεν σου επιστρέφει πολλά πράγματα για να τσεκάρεις πιθανά σφάλματα μετατροπής, αλλά σε βάζει στο νόημα.

Δοκίμασε π.χ. να γράψεις κάτι σαν το παρακάτω:
Κώδικας: Επιλογή όλων

double f;
f = atof("+2.568976");
printf("%g\n", f);


Δοκίμασε να την μπερδέψεις κιόλας με διαφορετικά strings να δεις πως αντιδράει. Μετά, αφού μπεις στο νόημα, δοκίμασε το ίδιο με την strtod(), και για να την κρατήσεις απλή πέρνα της το 2ο όρισμα ως NULL:
Κώδικας: Επιλογή όλων

double f;
f = strod("+2.568976", NULL);
printf("%g\n", f);

Δεν είναι κάτι το τραγικό δηλαδή, αυτό θέλω να πω. Άλλωστε όπως σου είπα, αργά ή γρήγορα θα αναγκαστείς να διαβάζεις όλα τα input ως strings και να τα μετατρέπεις μετά σε ότι θέλεις.
Οι συναρτήσεις μετατροπής είναι όλες στην <stdlib.h>

Ότι δυσκολία συναντήσεις είμαστε πάντα πρόθυμοι να βοηθήσουμε, εγώ τουλάχιστον. Κάνε πρώτα όπως το είχες στην αρχή το πρόγραμμα να διαβάζει ένα αριθμό την φορά, αλλά ως string που θα το μετατρέπεις σε double (αρχικά με την atof() για να μπεις στο νόημα, και πιο μετά με την strtod()).

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

Στο νήμα με τους Υπολογισμούς μεταξύ Ημερομηνιών, έχω συμπεριλάβει στον κώδικα συναρτήσεις γενικής διαχείρισης strings, όπως η s_trim() που σβήνει κενά από την αρχή και το τέλος ή την s_tolower() που μετατρέπει ένα string σε πεζά γράμματα, κλπ.... τις οποίες μπορείς να τις χρησιμοποιήσεις ατόφιες σε οποιοδήποτε πρόγραμμα.

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

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό migf1 » 20 Ιουν 2011, 22:31

Λοιπόν, δες για παράδειγμα τον παρακάτω κώδικα, που σου ζητάει να γράψεις έναν αριθμό, τον διαβάζει ως string και κατόπιν τον μετατρέπει σε double πρώτα με την atof() και κατόπιν με την strtod().

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

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

#define MAX_LINE 255+1

// -----------------------------------------------------------------------------------
char *s_get(char s[], int maxlen)
{
register int i;

for (i=0; (s[i]=getchar()) != '\n' && i < maxlen-1; i++)
; // for-loop with empty body
s[i] = '\0'; // null-terminate s

return s;
}

// -----------------------------------------------------------------------------------
int main( void )
{
char line[MAX_LINE];
double num = 0.0;

printf("Enter any real number: ");
fflush(stdin);
s_get( line, MAX_LINE);
printf("You entered the string: %s\n", line);

num = atof( line );
printf("\nConverted to double with atof() it gives: %g\n", num);

num = strtod( line, NULL );
printf("Converted to double with strtod() it gives: %g\n", num);

printf("\npress ENTER to exit..."); fflush(stdin); getchar();
exit(0);
}

Την s_get() στην έκανα επίτηδες να μη χρησιμοποιεί συμβολισμούς δεικτών, για να μη σε μπερδεύει. Αλλά είτε έτσι, είτε αλλιώς, χρησιμοποίησέ την ατόφια χωρίς να σε ενοχλεί ο εσωτερικό της κώδικας (είναι σαν να χρησιμοποιείς π.χ. την getline() του gcc, η οποία όμως δεν υπάρχει σε άλλους compilers).

Το ενδιαφέρον που παρουσιάζει ο παραπάνω κώδικας είναι πώς τόσο η atof() όσο και η strotod() παίρνουν το διαβασμένο string line και το μετατρέπουν σε double num. Αν το line περιείχε άκυρα πράματα (γράμματα, κλπ) τότε επιστρέφουν 0, αν περιείχε ανάμιξη αριθμών με γραμμάτων με τους αριθμούς μπροστά τότε μετατρέπονται μόνο οι αριθμοί, αλλιώς επιστρέφουν τον πραγματικό αριθμό που αντιστοιχεί σε αυτό που έγραψε ο χρήστης.

Μάλιστα, κατά την μετατροπή, αγνοούν τυχόν κενά στην αρχή του string. Επίσης, αν η γραμμή περιείχε περισσότερα από ένα πράγματα, δαιχωρισμένα με κενά, η μετατροπή λαμβάνει υπόψη της μονάχα το 1ο από αυτά.

Από τη στιγμή λοιπόν που και οι δυο αυτές συναρτήσεις ανήκουν στην στάνταρ βιβλιοθήκη της C (stdlib) μπορούμε να υποθέσουμε με ασφάλεια πως αυτή την συμπεριφορά την διατηρούν σε όλους τους compilers, σε όλες τις πλατφόρμες :)

Το πρόβλημα που υπάρχει είναι πως σε περίπτωση σφάλματος, επιστρέφουν την τιμή 0. Οπότε αν ο χρήστης είχε γράψει 0 στο string line, τότε δεν έχουμε τρόπο να ξέρουμε αν το 0 που μας επιστρέφει η μετατροπή είναι έγκυρη τιμή ή προϊόν σφάλματος.

Και με την atof() δεν έχουμε κιόλας τρόπο να το ελέγξουμε. Βέβαια στις περισσότερες περιπτώσεις ίσως να μη χρειάζεται καν να ελέγξουμε (και απλά να εχουμε γράψει στην τεκμηρίωση του προγράμματός μας πως τυχόν άκυρες τιμές στο line μετατρέπονται πάντα σε 0 ;)

Αν όμως θέλουμε όντως να ελέγξουμε, μπορούμε να το κάνουμε μέσω της strtod(), με χρήση του 2ου ορίσματός της (τώρα το έχουμε NULL στον κώδικα) σε συνδυασμό με την καθολική μεταβλητή errno που ορίζεται στη στάνταρ βιβλιοθήκη: <errno.h>.

Την μεταβλητή errno την χρησιμοποιούν όλες οι στάνταρ συναρτήσεις για να επισημάνουν τυχόν σφάλματα που προέκυψαν κατά την εκτέλεσή τους, με συγκεκριμένες τιμές που εκχωρούν στην errno (δες το παραπάνω link για αυτές τις τιμές.... ουσιαστικά είναι θετικές σταθερές, καθορισμένες με #define μέσα στο errno.h που ξεκινάνε πάντα με το γράμμα E). Το στάνταρ υποχρεώνει να έχουν καθοριστεί τουλάχιστον 3 τέτοιες τιμές:
  • EDOM (σημαίνει: Mathematics argument out of domain of function)
  • EILSEQ (σημαίνει: Illegal byte sequence.)
  • ERANGE (σημαίνει: Result too large.)
οπότε αυτές υπάρχουν σε όλους τους compilers. Υπάρχουν και πολλές άλλες, ανάλογα τον compiler, αλλά τουλάχιστον οι 3 παραπάνω είναι στάνταρ σε όλους!

Ο πιο σίγουρος τρόπος να ελέγχουμε τη μεταβλητή errno είναι να την μηδενίζουμε πριν καλέσουμε την όποια συνάρτηση, και μετά το τέλος της συνάρτησης να ελέγχουμε αν έχει αλλάξει η τιμή του errno.

Επιστρέφοντας τώρα στην strtod(), αν διαβάσεις την τεκμηρίωσή της θα δεις πως για 2ο όρισμα αντί για NULL μπορούμε να της περάσουμε τη διεύθυνση ενός char pointer (δείκτης χαρακτήρων) ο οποίος αν η μετατροπή είναι επιτυχημένη τότε περιέχει τον μηδενικό χαρακτήρα ('\0').

Αλλιώς, εκτός από το 0 που επιστρέφει η συνάρτηση, βάζει και το 2ο όρισμά της να δείχνει είτε στο αρχικό string line (αν περιείχε μονάχα κενά ή/και άκυρα πράματα) είτε στο άκυρο μέρος του string line (π.χ. αν το line περιείχε: "123abcd" η συνάρτηση μετατρέπει κι επιστρέφει ως double το "123" και βάζει το 2ο όρισμα να δείχνει στο "abcd").

Επιπρόσθετα, αν τα έγκυρα πράματα του line αντιστοιχούν σε αριθμό που είναι έξω από το εύρος των αριθμών που μπορεί να απεικονίσει ο τύπος double, τότε πάει και βάζει στο errno την τιμή: ERANGE

Οπότε, για να ελέγξουμε αν η strtod() απέτυχε (ή αν... μισοπέτυχε :lol:), μετά το κάλεσμά της ελέγχουμε δυο πράγματα:
  1. αν το errno ισούται με ERANGE
  2. αν ο πρώτος χαρακτήρας το 2ου ορίσματος της είναι διάφορος του '\0'
Όλα τα παραπάνω τα χρησιμοποιώ στην συνάρτηση: num_askuser() στον κώδικά μου στην 1η δημοσίευση, αλλά για να είναι πιο ευανάγνωστα, ας τα ενσωματώσουμε πιο απλοποιημένα στο παράδειγμα κώδικα αυτής εδώ της δημοσίευσης:
Κώδικας: Επιλογή όλων

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

#define MAX_LINE 255+1

// -----------------------------------------------------------------------------------
char *s_get(char s[], int maxlen)
{
register int i;

for (i=0; (s[i]=getchar()) != '\n' && i < maxlen-1; i++)
; // for-loop with empty body
s[i] = '\0'; // null-terminate s

return s;
}

// -----------------------------------------------------------------------------------
int main( void )
{
extern int errno; // defined in <errno.h>
char line[MAX_LINE];
char *dummy;
double num = 0.0;

printf("Enter any real number: ");
fflush(stdin);
s_get( line, MAX_LINE);
printf("You entered the string: %s\n", line);

num = atof( line );
printf("\nConverted to double with atof() it gives: %g\n", num);

errno = 0;
num = strtod( line, &dummy );
printf("\nConverted to double with strtod() it gives: %g\n", num);
if (errno == ERANGE)
puts("\t*** error: number too big or too small");
if ( *dummy != '\0')
printf("\t*** info: \"%s\" was invalid so it was ignored\n", dummy);

printf("\npress ENTER to exit..."); fflush(stdin); getchar();
exit(0);
}

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

Re: Calculator γραμμένο σε C

Δημοσίευσηαπό UnKnown96 » 20 Ιουν 2011, 23:00

Λοιπόν επειδή είναι βράδυ, και κοιμάμε όρθιος θα τα διαβάσω αύριο και βλέπουμε.
Όντως είναι πράγματα που χρειάζονται και δουλεύουν και στις δύο πλατφόρμες.
Ποιός θα έλεγε ότι μπορείς να μάθεις τόσα πολλά πράγματα φτιάχνωντας μια αριθμομηχανή και ακόμα ποιό πολλά αφού αρχίσεις να φτιάχνεις τα bugs.
Ιδικά τώρα που το σκέφτομαι σαν πρώτο πρόγραμμα που το έκανα ( ιδικά η V1 ) ήταν τόσο χάλια που όλα τα είχα Integer και αναροτιόμουν γιατί δεν έχει δεκαδικούς. :D
Όπως και να 'χει πάω για ύπνο, βλέπουμε !
Άβαταρ μέλους
UnKnown96
dudeTUX
dudeTUX
 
Δημοσιεύσεις: 370
Εγγραφή: 08 Ιουν 2010, 15:23
Τοποθεσία: Ρόδος
Εκτύπωση

ΠροηγούμενηΕπόμενο

Επιστροφή στο Ανάπτυξη Λογισμικού / Αλγόριθμοι