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

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

Δημοσ.

Σε μεγάλα projects δεν είναι καθόλου δύσκολο να την πατήσεις κάπως έτσι. Για αυτό και μιλάμε για good και bad practices, τα οποία μπορεί στις απλές ασκήσεις να μη φαίνεται η σημασία τους, αλλά σε μεγάλα projects αποκτούν ολοένα και μεγαλύτερη σημασία. Αν εντυπωθούν εξαρχής bad practices, αυξάνονται οι πιθανότητες εμφάνισης δυσεύρετων bugs όσο μεγαλώνει ο κώδικας.

 

1.Στο παραπάνω παράδειγμα, το int size μπορεί να μην είναι καν ορισμένο στο ίδιο αρχείο με την σταθερά SIZE. Για τον ίδιο λόγο είναι σημαντικό να εντυπώσει κανείς εξαρχής good-practices ακόμα και στη μεθοδολογία ονομασίας των μεταβλητών, συναρτήσεων, σταθερών, κλπ που χρησιμοποιεί.

 

EDIT:

 

2.Ξέχασα, ακόμα και σε αυτό το μπακαλο-παράδειγμα μπορείς να αποφύγεις το BOOΜ είτε υλοποιώντας την foo() με τον λιτό τρόπο, είτε με τον περιφραστικό, με την προϋπόθεση πως στον περιφραστικό κάνεις έλεγχο μέσα στον κώδικά της συνάρτησης για να είναι το όρισμα στο επιθυμητό εύρος τιμών πριν το χρησιμοποιήσεις.

 

1. Οποτε και μεγαλώνει η πιθανοτητα να την πατήσεις :P

2. Δεν ξέχασες κατι... γιατι αυτο που έδωσες ειναι παράδειγμα που δειχνει

την χρησιμότητα του ελέγχου μεσα στη συνάρτηση.

  • Απαντ. 1,6k
  • Δημ.
  • Τελ. απάντηση

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

Δημοσ.

...

2. Δεν ξέχασες κατι... γιατι αυτο που έδωσες ειναι παράδειγμα που δειχνει

την χρησιμότητα του ελέγχου μεσα στη συνάρτηση.

Ή του λιτού τρόπου χωρίς έλεγχο.

Δημοσ.

Ή του λιτού τρόπου χωρίς έλεγχο.

 

Του περιφραστικού χωρις έλεγχο θες να πεις :P

Ο λιτος δεν παίρνει 2ο ορισμα στην κληση.

Δημοσ.

Του περιφραστικού χωρις έλεγχο θες να πεις :P

Ο λιτος δεν παίρνει 2ο ορισμα στην κληση.

Όχι, εννοώ πως ο λιτός (που δεν παίρνει όρισμα και δεν κάνει έλεγχο) ισοδυναμεί στην αποφυγή του BOOM με τον περιφραστικό (που παίρνει όρισμα και κάνει έλεγχο).

Δημοσ.

Όχι, εννοώ πως ο λιτός (που δεν παίρνει όρισμα και δεν κάνει έλεγχο) ισοδυναμεί στην αποφυγή του BOOM με τον περιφραστικό (που παίρνει όρισμα και κάνει έλεγχο).

 

αααααααααααα... οκ αφεντικο. Οτι πεις :P

παω καμια τσάρκα. Τα λεμε παιδια , ευχαριστω και τον defacer.

Δημοσ.

Αν και δεν μαρέσουν οι απόλυτες απόψεις και έχω διαφωνίσει σε επιχειρήματα τύπου "μην χρησιμοποιείτε ποτέ 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 είναι σαν τον τροχό του ξυλουργού: αν πας να φτιάξεις μόνος σου χωρίς να ξέρεις, κομμένο χέρι. Αν πας να χρησιμοποιήσεις ένα έτοιμο καλοφτιαγμένο χωρίς να ξέρεις, πάλι κομμένο χέρι.

Δημοσ.

@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, γιατί το έκανα στο πόδι).

Δημοσ.

Συγκριτικά με του 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.

Δημοσ.

O κώδικας είναι σίγουρα πιο αργός από του King διότι π.χ. χρησιμοποιεί strchr() σε string literals και κατόπιν αφαίρεση των διευθύνσεων 2 δεικτών, αντί για τα απλά case και την απλή ανάθεση char στον κώδικα του King. Επίσης χρησιμοποιεί την tolower().

 

Στην προκειμένη περίπτωση δεν μας ενδιαφέρει η ταχύτητα, ούτε διαβάζουμε massive data από κάποιο stream, αλλά είναι χρήσιμο να γνωρίζει από τώρα ο Starlight και όποιος διαβάζει το νήμα πως αν σκοπεύει να ασχοληθεί με speed critical εφαρμογές θα πρέπει να το σκεφτεί και 1 και 2 και 3 φορές πριν χρησιμοποιήσει την εν λόγω "τεχνική" και κυρίως που και πως θα την χρησιμοποιήσει (αν την χρησιμοποιήσει).

Δημοσ.

Σε περίπτωση που θέλουμε να συγκρίνουμε αν ένας floating number ειναι 0

 

αυτο εδω ειναι σωστο ->

 

> if ( x == 0 ) 

 

:S

 

Πχ πρεπει να βάζουμε 0.0 ???

 

> if ( x > 0.0) 

Δημοσ.

Σε περίπτωση που θέλουμε να συγκρίνουμε αν ένας 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 θα αλλάξει την τιμή του (δηλαδή μπακάλικα θα μπορούσαμε να πούμε ο μικρότερος αριθμός που μπορεί να αναπαρασταθεί).

Δημοσ.

Ερώτηση:μερικές φορές,ο μεταγλωττιστής μου βγάζει το εξής σφάλμα(το γράφω περιληπτικά γιατί δεν το θυμάμαι):"scanf is .....Use scanf_s instead").Τι παίζει με το _s(νομίζω ότι ο migf1 έχει ποστάρει παλιότερα κάτι σχετικό);

Επισκέπτης
Αυτό το θέμα είναι πλέον κλειστό για περαιτέρω απαντήσεις.

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