Star_Light Δημοσ. 19 Ιουλίου 2012 Δημοσ. 19 Ιουλίου 2012 Σε μεγάλα projects δεν είναι καθόλου δύσκολο να την πατήσεις κάπως έτσι. Για αυτό και μιλάμε για good και bad practices, τα οποία μπορεί στις απλές ασκήσεις να μη φαίνεται η σημασία τους, αλλά σε μεγάλα projects αποκτούν ολοένα και μεγαλύτερη σημασία. Αν εντυπωθούν εξαρχής bad practices, αυξάνονται οι πιθανότητες εμφάνισης δυσεύρετων bugs όσο μεγαλώνει ο κώδικας. 1.Στο παραπάνω παράδειγμα, το int size μπορεί να μην είναι καν ορισμένο στο ίδιο αρχείο με την σταθερά SIZE. Για τον ίδιο λόγο είναι σημαντικό να εντυπώσει κανείς εξαρχής good-practices ακόμα και στη μεθοδολογία ονομασίας των μεταβλητών, συναρτήσεων, σταθερών, κλπ που χρησιμοποιεί. EDIT: 2.Ξέχασα, ακόμα και σε αυτό το μπακαλο-παράδειγμα μπορείς να αποφύγεις το BOOΜ είτε υλοποιώντας την foo() με τον λιτό τρόπο, είτε με τον περιφραστικό, με την προϋπόθεση πως στον περιφραστικό κάνεις έλεγχο μέσα στον κώδικά της συνάρτησης για να είναι το όρισμα στο επιθυμητό εύρος τιμών πριν το χρησιμοποιήσεις. 1. Οποτε και μεγαλώνει η πιθανοτητα να την πατήσεις 2. Δεν ξέχασες κατι... γιατι αυτο που έδωσες ειναι παράδειγμα που δειχνει την χρησιμότητα του ελέγχου μεσα στη συνάρτηση.
migf1 Δημοσ. 19 Ιουλίου 2012 Δημοσ. 19 Ιουλίου 2012 ... 2. Δεν ξέχασες κατι... γιατι αυτο που έδωσες ειναι παράδειγμα που δειχνει την χρησιμότητα του ελέγχου μεσα στη συνάρτηση. Ή του λιτού τρόπου χωρίς έλεγχο.
Star_Light Δημοσ. 19 Ιουλίου 2012 Δημοσ. 19 Ιουλίου 2012 Ή του λιτού τρόπου χωρίς έλεγχο. Του περιφραστικού χωρις έλεγχο θες να πεις Ο λιτος δεν παίρνει 2ο ορισμα στην κληση.
migf1 Δημοσ. 19 Ιουλίου 2012 Δημοσ. 19 Ιουλίου 2012 Του περιφραστικού χωρις έλεγχο θες να πεις Ο λιτος δεν παίρνει 2ο ορισμα στην κληση. Όχι, εννοώ πως ο λιτός (που δεν παίρνει όρισμα και δεν κάνει έλεγχο) ισοδυναμεί στην αποφυγή του BOOM με τον περιφραστικό (που παίρνει όρισμα και κάνει έλεγχο).
Star_Light Δημοσ. 19 Ιουλίου 2012 Δημοσ. 19 Ιουλίου 2012 Όχι, εννοώ πως ο λιτός (που δεν παίρνει όρισμα και δεν κάνει έλεγχο) ισοδυναμεί στην αποφυγή του BOOM με τον περιφραστικό (που παίρνει όρισμα και κάνει έλεγχο). αααααααααααα... οκ αφεντικο. Οτι πεις παω καμια τσάρκα. Τα λεμε παιδια , ευχαριστω και τον defacer.
defacer Δημοσ. 20 Ιουλίου 2012 Δημοσ. 20 Ιουλίου 2012 Αν και δεν μαρέσουν οι απόλυτες απόψεις και έχω διαφωνίσει σε επιχειρήματα τύπου "μην χρησιμοποιείτε ποτέ goto" και τέτοια, στην προκειμένη περίπτωση θα πω κάτι πιο ακραίο από το "να χρησιμοποιείς παντού crng". Αυτό που προτείνω είναι "Μην υλοποιείς RNG". Τα μαθηματικά είναι δύσκολα και είναι πολύ εύκολο να ξεφύγει κάτι όπως το φιάσκο του debian, οι αδυναμίες του DSA, κτλ. Όπως είπε και το κογιότ σε ένα επεισόδιο "Άσε να το κάνει κάποιος που ξέρει". Υπάρχουν δεκάδες βιβλιοθήκες σε κάθε γλώσσα με αποδεδειγμένα σωστό κώδικα. Δεν αναφερόμασταν ακριβώς σ' αυτό που νομίζω ότι κατάλαβες. Εννοείται ότι δε θα πας να υλοποιήσεις crypto-strength RNG για να βάλεις κρυπτογραφία, αν μη τι άλλο για να γίνει σωστά αυτή η δουλειά πρέπει να γνωρίζεις πολύ περισσότερα μαθηματικά και crypto-theory από προγραμματισμό (δε νομίζω ότι υπάρχει κανείς εδώ που να έχει τέτοιου επιπέδου παρα-προγραμματιστικές γνώσεις). Βέβαια αν θέλεις ένα "γενικής χρήσης" RNG δεν είναι καθόλου δύσκολο αρκεί να σκαμπάζεις λίγο μαθηματικά, ορίστε ένας LCG σε C (10 γραμμές στην ουσία). Το θέμα είναι πως ακόμα και όλα έτοιμα να τα πάρεις, πρέπει να ξέρεις πώς να τα χρησιμοποιήσεις. Aν δεν καταλαβαίνεις βασικά πράγματα για τους τρόπους λειτουργίας των symmetric ciphers (τι απαιτήσεις έχουν από σένα για να κάνουν επιτυχώς αυτό που γράφει στη συσκευασία) τότε δεν πρόκειται να σε σώσει κανένας RNG. Ή αν δεν ξέρεις τι είναι replay attack και πώς να προστατευτείς από αυτήν. Αυτό εννοούσα σε κάποιο σημείο που είχα γράψει (στο άλλο thread νομίζω) "secure σύστημα != copy paste AES και SHA". Γενικά δηλαδή η βιβλιοθήκη crypto είναι σαν τον τροχό του ξυλουργού: αν πας να φτιάξεις μόνος σου χωρίς να ξέρεις, κομμένο χέρι. Αν πας να χρησιμοποιήσεις ένα έτοιμο καλοφτιαγμένο χωρίς να ξέρεις, πάλι κομμένο χέρι.
migf1 Δημοσ. 20 Ιουλίου 2012 Δημοσ. 20 Ιουλίου 2012 @Starlight: Μια πολύ πρόχειρη παραλλαγή του κώδικα του King που διατηρεί την ίδια λογική, το ίδιο πλήθος συναρτήσεων, χρησιμοποιεί την ίδια ύλη (π.χ. χωρίς structs), κλπ αλλά δεν έχει καθόλου globals... > #include <stdbool.h> /* C99 only */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #define NRANKS 13 #define NSUITS 4 #define NCARDS 5 #define RANKS "23456789tjqka" #define SUITS "cdhs" /* ή με defines αν δεν έχει μιλήσει για enums */ enum { NSTRAIGHTS = 0, NFLUSHES, NQUADS, NTRIPLES, NPAIRS, /* */ MAX_RESULTS }; /*** no external variables ***/ //int num_in_rank[NRANKS]; //int num_in_suit[NSUITS]; //bool straight, flush, four, three; //int npairs; /* can be 0, 1, or 2 */ /* prototypes */ void read_cards( int num_in_rank[], int num_in_suit[] ); void analyze_hand( int num_in_rank[], int num_in_suit[], int result[] ); void print_result( int result[] ); /********************************************************** * main: Calls read_cards, analyze_hand, and print_result * * repeatedly. * **********************************************************/ int main( void ) { int num_in_rank[ NRANKS ] = {0}; int num_in_suit[ NSUITS ] = {0}; int result[ MAX_RESULTS ] = {0}; for (; { read_cards( num_in_rank, num_in_suit ); analyze_hand( num_in_rank, num_in_suit, result ); print_result( result ); } } /********************************************************** * read_cards: Reads the cards into the external * * variables num_in_rank and num_in_suit; * * checks for badcard cards and duplicate cards. * **********************************************************/ void read_cards( int num_in_rank[], int num_in_suit[] ) { bool card_exists[NRANKS][NSUITS]; char c, crank, csuit; char *cp = NULL; int rank, suit; bool badcard; int ncards = 0; /* reset num_in_rank[] && card_exists[][] */ for (rank = 0; rank < NRANKS; rank++) { num_in_rank[rank] = 0; for (suit = 0; suit < NSUITS; suit++) card_exists[rank][suit] = false; } /* reset num_in_suit[] */ for (suit = 0; suit < NSUITS; suit++) num_in_suit[suit] = 0; while ( ncards < NCARDS ) { badcard = false; /* reset */ printf("Enter a card: "); /* read rank */ crank = tolower( getchar() ); if ( '0' == crank ) exit( EXIT_SUCCESS ); if ( NULL == (cp=strchr(RANKS, crank)) ) badcard = true; else rank = cp - RANKS; /* read suit */ csuit = tolower( getchar() ); if ( NULL == (cp=strchr(SUITS, csuit)) ) badcard = true; else suit = cp - SUITS; while ( '\n' != (c = getchar()) ) if ( ' ' != c ) badcard = true; if ( badcard ) printf("Bad card; ignored.\n"); else if ( card_exists[rank][suit] ) printf("Duplicate card; ignored.\n"); else { num_in_rank[rank]++; num_in_suit[suit]++; card_exists[rank][suit] = true; ncards++; } } } /********************************************************** * analyze_hand: Determines whether the hand contains a * * straight, a flush, four-of-a-kind, * * and/or three-of-a-kind; determines the * * number of pairs; stores the results into * * the external variables straight, flush, * * four, three, and npairs. * **********************************************************/ void analyze_hand( int num_in_rank[], int num_in_suit[], int result[] ) { int nconsec = 0; // # of cards with consecutive ranks int rank, suit; /* reset results */ for (int i=0; i < MAX_RESULTS; i++) result[i] = 0; /* check for flush */ for (suit = 0; suit < NSUITS; suit++) if ( NCARDS == num_in_suit[suit] ) result[ NFLUSHES ] = 1; /* check for straight */ rank = 0; while ( 0 == num_in_rank[rank] ) rank++; for (; rank < NRANKS && num_in_rank[rank] > 0; rank++) nconsec++; if ( NCARDS == nconsec) { result[ NSTRAIGHTS ] = 1; return; } /* check for 4-of-a-kind, 3-of-a-kind, and pairs */ for (rank = 0; rank < NRANKS; rank++) { switch ( num_in_rank[ rank ] ) { case 4: result[ NQUADS ] = 1; break; case 3: result[ NTRIPLES ] = 1; break; case 2: result[ NPAIRS ]++; break; default: break; } } } /********************************************************** * print_result: Prints the classification of the hand, * * based on the values of the external * * variables straight, flush, four, three, * * and npairs. * **********************************************************/ void print_result( int result[] ) { if ( result[ NSTRAIGHTS ] && result[ NFLUSHES ] ) printf("Straight flush"); else if ( result[ NQUADS ] ) printf("Four of a kind"); else if ( result[ NTRIPLES ] && 1 == result[ NPAIRS ] ) printf("Full house"); else if ( result[ NFLUSHES ] ) printf("Flush"); else if ( result[ NSTRAIGHTS ] ) printf("Straight"); else if ( result[ NTRIPLES ] ) printf("Three of a kind"); else if ( 2 == result[ NPAIRS ] ) printf("Two pairs"); else if ( 1 == result[ NPAIRS ] ) printf("Pair"); else printf("High card"); printf("\n\n"); } Η βασική διαφοροποίηση είναι πως τα αποτελέσματα αντί να τα έχω χύμα μεταβλητές, τα βάζω ως στοιχεία ενός πίνακα result[ ] με ονοματισμένες τις θέσεις των στοιχείων του (μέσω του enum) ώστε να μπορώ να τον περνάω εύκολα ως όρισμα (αντί να περνάω όλες εκείνες τις μεταβλητές). Με παρόμοια λογική σύμπτυξης, έχω γκρουπάρει τα ranks και τα suits σε string literals. Συγκριτικά με του King αυτός ο κώδικας είναι πιο αργός, αλλά έτσι κι αλλιώς δεν πρόκειται για speed critical πρόγραμμα (επίσης ενδεχομένως να έχω και τίποτα bugs, γιατί το έκανα στο πόδι).
defacer Δημοσ. 20 Ιουλίου 2012 Δημοσ. 20 Ιουλίου 2012 Συγκριτικά με του King αυτός ο κώδικας είναι πιο αργός, αλλά έτσι κι αλλιώς δεν πρόκειται για speed critical πρόγραμμα (επίσης ενδεχομένως να έχω και τίποτα bugs, γιατί το έκανα στο πόδι). @Starlight: Ο κώδικας αυτός είναι πιθανότατα (όχι σίγουρα) πιο αργός, και πιο συγκεκριμένα γύρω στα 5 nanosecond αργότερος (νούμερο που προέκυψε από επιστημονικό profiling με το μάτι). Ο migf1 έχει δίκιο με την έννοια ότι ένα αμάξι με τελική 400 είναι πιο αργό από ένα με τελική 401, αλλά νομίζω ότι καταλαβαίνεις που το πάω. Το να λάβεις υπόψη την ταχύτητα εδώ είναι σα να λαμβάνεις υπόψη όταν αγοράζεις παντελόνι πόση ώρα έκαναν να το ράψουνε. Δε λέω κάτι καινούριο, αλλά I can not stress how important it is να μη μπαίνει σαν κριτήριο η ταχύτητα σε τέτοιες περιπτώσεις. Update: Θα σε βοηθήσει ίσως στη συγκρότηση σκέψης να έχεις υπόψη ότι στην προκειμένη η ταχύτητα του κώδικα δεν παίζει ρόλο γιατί οτι και να κάνεις το πρόγραμμά σου θα είναι I/O bound και όχι CPU bound.
migf1 Δημοσ. 20 Ιουλίου 2012 Δημοσ. 20 Ιουλίου 2012 O κώδικας είναι σίγουρα πιο αργός από του King διότι π.χ. χρησιμοποιεί strchr() σε string literals και κατόπιν αφαίρεση των διευθύνσεων 2 δεικτών, αντί για τα απλά case και την απλή ανάθεση char στον κώδικα του King. Επίσης χρησιμοποιεί την tolower(). Στην προκειμένη περίπτωση δεν μας ενδιαφέρει η ταχύτητα, ούτε διαβάζουμε massive data από κάποιο stream, αλλά είναι χρήσιμο να γνωρίζει από τώρα ο Starlight και όποιος διαβάζει το νήμα πως αν σκοπεύει να ασχοληθεί με speed critical εφαρμογές θα πρέπει να το σκεφτεί και 1 και 2 και 3 φορές πριν χρησιμοποιήσει την εν λόγω "τεχνική" και κυρίως που και πως θα την χρησιμοποιήσει (αν την χρησιμοποιήσει).
Star_Light Δημοσ. 22 Ιουλίου 2012 Δημοσ. 22 Ιουλίου 2012 Σε περίπτωση που θέλουμε να συγκρίνουμε αν ένας floating number ειναι 0 αυτο εδω ειναι σωστο -> > if ( x == 0 ) :S Πχ πρεπει να βάζουμε 0.0 ??? > if ( x > 0.0)
imitheos Δημοσ. 22 Ιουλίου 2012 Δημοσ. 22 Ιουλίου 2012 Σε περίπτωση που θέλουμε να συγκρίνουμε αν ένας floating number ειναι 0 αυτο εδω ειναι σωστο -> > if ( x == 0 ) :S Πχ πρεπει να βάζουμε 0.0 ??? > if ( x > 0.0) Και το x == 0 και το x == 0.0 θα παίξουν σωστά. Γενικά όμως έχε υπόψην ότι ουσιαστικά δεν έχουμε πραγματικούς αριθμούς αλλά μια "εξομοίωση" τους με βάση κάποια αναπαράσταση (συνήθως ieee754). Αυτό γίνεται γιατί έχουμε άπειρους πραγματικούς και πεπερασμένο εύρος μεταβλητών. Συνέπεια αυτού είναι να μην μπορούν να αναπαρασταθούν όλοι οι αριθμοί. Όταν χρησιμοποιείς παντού ίδιο τύπο μεταβλητών (πχ double) τότε δεν έχεις ιδιαίτερα προβλήματα αλλά θέλει λίγη προσοχή. Όταν μπλέκουν τύποι τότε είναι πιο εύκολο να γίνει πατάτα. > #include <stdio.h> int main(void) { float f; double d; f = 0.0; d = 0.0; printf("0.0 %d\n", f == d); f = 2.3; d = 2.3; printf("2.3 %d\n", f == d); f = 2.5; d = 2.5; printf("2.5 %d\n", f == d); return 0; } Έξοδος: 0.0 1 2.3 0 2.5 1 Χάριν ευκολίας, ας ξεχάσουμε NaN, άπειρο, κτλ και ας σκεφτούμε μόνο απλούς πραγματικούς αριθμούς. Δες το παραπάνω χαζό πρόγραμμα. Το 0.0 και το 2.5 μπορούν να αναπαρασταθούν σε ένα float οπότε η σύγκριση παίζει σωστά και παίρνουμε αποτέλεσμα 1. Το 2.3 όμως δεν μπορεί να αναπαρασταθεί σωστά οπότε στην περίπτωση του float μπορεί να είναι πχ 2.222222229991 ενώ σε double 2.2222222299999997 με αποτέλεσμα οι δύο αριθμοί να μην είναι ίσοι. Μια μέθοδος που προτείνεται στο C Faq αλλά δεν είναι πανάκεια είναι η ακόλουθη: > fabs(a - <= epsilon * fabs(a) όπου epsilon είναι η ακρίβεια που θέλεις για την σύγκριση. Αν διαβάσεις το αρχείο /usr/lib(ή lib64)/gcc/host_designation (πχ x86_64-arch-linux-gnu)/έκδοση (πχ 4.7.0)/include/float.h, θα δεις να ορίζει > /* The difference between 1 and the least value greater than 1 that is representable in the given floating point type, b**1-p. */ #define FLT_EPSILON __FLT_EPSILON__ #define DBL_EPSILON __DBL_EPSILON__ #define LDBL_EPSILON __LDBL_EPSILON__ τα οποία όπως βλέπεις είναι ο μικρότερος αριθμός που αν τον προσθέσουμε στο 1.0 θα αλλάξει την τιμή του (δηλαδή μπακάλικα θα μπορούσαμε να πούμε ο μικρότερος αριθμός που μπορεί να αναπαρασταθεί).
migf1 Δημοσ. 22 Ιουλίου 2012 Δημοσ. 22 Ιουλίου 2012 Ρίξε μια ματιά κι εδώ: http://x-karagiannis.gr/prog/libs/content/misc/other/prompt_for/doc/html/index.html#Err (το 2ο μισό της ενότητας Σφάλματα)
Star_Light Δημοσ. 22 Ιουλίου 2012 Δημοσ. 22 Ιουλίου 2012 τΟ ΞΕΡΩ το ΙΕΕΕ754 το ειχα ψαξει παλιοτερα ! Ευχαριστω παιδια... θα διαβάσω και αυτα που μου δώσατε.
capoelo Δημοσ. 24 Ιουλίου 2012 Μέλος Δημοσ. 24 Ιουλίου 2012 Ερώτηση:μερικές φορές,ο μεταγλωττιστής μου βγάζει το εξής σφάλμα(το γράφω περιληπτικά γιατί δεν το θυμάμαι):"scanf is .....Use scanf_s instead").Τι παίζει με το _s(νομίζω ότι ο migf1 έχει ποστάρει παλιότερα κάτι σχετικό);
Προτεινόμενες αναρτήσεις