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

Ερωτήσεις για C


capoelo

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

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

 

 

Σπαζοκεφαλιές για ανώμαλους :P

 

Εχεις απολυτο δικιο. Το ξεχναω

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

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

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

Σε αυτές τις περιπτώσεις δεν τα χρειάζεσαι καθόλου ρε συ, μιας και μιλάμε για platform specific coding. Οπότε όταν ξέρεις πως στην πλατφόρμα σου είναι π.χ. ο char 1 byte, ο short 2 και ο int 4, κλπ, δεν υπάρχει λόγος να χρησιμοποιήσεις καθόλου το <inttypes.h>

 

 

Ακριβώς!

 

Και όταν έχεις κώδικα που είναι μεν για μικρές μνήμες αλλά θα πρέπει να είναι portable (σε διαφορετικές πλατφόρμες/boards - επεξεργαστές) τότε τα typedefs είναι αρκετά καλά για το readability. Βάζοντας και hex formats για constants (ιδιαίτερα όταν έχεις buffers να γεμίζεις) είσαι καλυμμένος από types.

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

Σπαζοκεφαλιές για ανώμαλους :P

 

Εχεις απολυτο δικιο. Το ξεχναω

Ναι! Γιατί να μπλέξεις 3 διαφορετικούς τύπους μέσα στην ίδια αριθμητική παράσταση; doing so you are looking for trouble (που λένε κι οι Αμερικάνοι).

 

Ακριβώς!

 

Και όταν έχεις κώδικα που είναι μεν για μικρές μνήμες αλλά θα πρέπει να είναι portable (σε διαφορετικές πλατφόρμες/boards - επεξεργαστές) τότε τα typedefs είναι αρκετά καλά για το readability. Βάζοντας και hex formats για constants (ιδιαίτερα όταν έχεις buffers να γεμίζεις) είσαι καλυμμένος από types.

Τα typedefs όμως πάλι στηρίζονται στις platform specific υλοποιήσεις. Εννοώ πως δεν μπορείς να γράψεις truly portable κώδικα με σκέτα typdefs, hex'ed or not, χωρίς να χρησιμοποιήσεις το <inttypes.h>. Χρειάζεσαι κατ' ελάχιστον να έχεις φιξαρισμένο το μέγεθος του char σε όλες σου τις πλατφόρμες... αν το έχεις αυτό εξασφαλισμένο, τότε μπορείς με typedefs να φτιάξεις δικούς custom τύπους για short, int, long, κλπ.

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

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

 

 

Χε...

 

Έχω κάνει τέτοια... αλλά ήταν too specific..

 

 

(Forced fixed point arithmetics μέσα σε διεργασίες που έκαναν return αναγκαστικά floating point numbers, π.χ. cos και sin, και με έναν επεξεργαστή χωρίς DSP... ε.. δεν θες floating point arithmetics)

 

Τα typedefs όμως πάλι στηρίζονται στις platform specific υλοποιήσεις. Εννοώ πως δεν μπορείς να γράψεις truly portable κώδικα με σκέτα typdefs, hex'ed or not, χωρίς να χρησιμοποιήσεις το <inttypes.h>. Χρειάζεσαι κατ' ελάχιστον να έχεις φιξαρισμένο το μέγεθος του char σε όλες σου τις πλατφόρμες... αν το έχεις αυτό εξασφαλισμένο, τότε μπορείς με typedefs να φτιάξεις δικούς custom τύπους για short, int, long, κλπ.

 

 

True...

 

Αν και δεν το έχω ψάξει too much... νομίζω ότι το MISRA βοηθάει σε αυτό.

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

Όλα αυτά αναφέρονται στην ενότητα Conversions του προτύπου (Ενότητα 6.3 στην C99)

 

Καταρχήν έχουμε τις integer promotions. Με λίγα μπακάλικα λόγια, ο κανόνας αναφέρει πως όλοι τύποι που είναι μικρότεροι από int μετατρέπονται σε όλες τις εκφράσεις αυτόματα σε int (ή unsigned int αν δεν φτάνει ο int). Αυτό γίνεται για να μην γίνονται άσκοπες υπερχειλήσεις.

 

Ένα κλασικό παράδειγμα που δίνουν πολλοί είναι το παρακάτω:

>
unsigned char a, b, c;
a = 140;
b = 120;
c = (a +  / 2;

 

Το αποτέλεσμα της παραπάνω πράξης είναι 260 / 2 = 130 το οποίο χωρά σε ένα char. Το a + b όμως πριν διαιρέσουμε μας δίνει 260 οπότε αν η πράξη γινόταν με chars το 260 δεν θα χωρούσε και θα είχαμε πρόβλημα. Με την προαγωγή αυτή των μεταβλητών σε int, έχουμε σωστό αποτέλεσμα χωρίς προβλήματα.

 

Ας έρθουμε τώρα στις conversions που ρώτησες. Το πρότυπο αναφέρει τα εξής βαρετά:

 

— No two signed integer types shall have the same rank, even if they have the same

representation.

— The rank of a signed integer type shall be greater than the rank of any signed integer

type with less precision.

— The rank of long long int shall be greater than the rank of long int, which

shall be greater than the rank of int, which shall be greater than the rank of short

int, which shall be greater than the rank of signed char.

— The rank of any unsigned integer type shall equal the rank of the corresponding

signed integer type, if any.

 

Όπως είναι λογικό έχουμε long long > long > int > short > char. Επίσης η δυνατότητα προσήμου δεν επηρεάζει τον βαθμό δηλαδή unsigned int έχει ίδιο βαθμό με signed int.

 

Το γεγονός ότι το sizeof σου δίνει ίδιο νούμερο στους signed και unsigned τύπους δεν έχει καμμία σχέση με τις μετατροπές που γίνονται όταν έχουμε διαφορετικούς τύπους σε μία έκφραση. Όπως διαβάζεις στην 1η πρόταση, ο βαθμός δεν εξαρτάται από την αναπαράσταση.

 

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

Αναλυτικότερα έχουμε (παραλείποντας τους πραγματικούς αριθμούς χάριν ευκολίας-συντομίας):

 

6.3.1.8 Usual arithmetic conversions

1) If both operands have the same type, then no further conversion is needed.

 

Αν και οι δύο τελεσταίοι έχουν ίδιο τύπο, δεν γίνεται τίποτα (κάτι μας είπε τώρα :) )

 

2) Otherwise, if both operands have signed integer types or both have unsigned

integer types, the operand with the type of lesser integer conversion rank is

converted to the type of the operand with greater rank.

 

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

Αυτός είναι ο κανόνας που διέπει την 2η σου έκφραση. Στην έκφραση "long i, int j = 1000; i = (long) j * j" πολλαπλασιάζεις ένα long (ένα int τον οποίο έχεις κάνει cast σε long) με ένα int. Και οι δύο τύποι υποστηρίζουν πρόσημο και ο long έχει μεγαλύτερο βαθμό από τον int οπότε ο πολλαπλασιασμός γίνεται σαν να ήταν και οι δύο τελεσταίοι long.

 

3) Otherwise, if the operand that has unsigned integer type has rank greater or

equal to the rank of the type of the other operand, then the operand with

signed integer type is converted to the type of the operand with unsigned

integer type.

Αυτός ο κανόνας διέπει την πρώτη σου έκφραση. Στην "int i, long j, unsigned int k και i + (int)j * k" έχεις δύο int (ένα κανονικό int και έναν long που τον έχεις κάνει cast σε int) και έναν unsigned int. Ο unsigned int έχει βαθμό "ίσο ή μεγαλύτερο" από τους δύο int οπότε σύμφωνα με τον κανόνα 3, οι δύο int τύποι μετατρέπονται σε unsigned και όλη η έκφραση έχει αποτέλεσμα unsigned όπως σωστά είπες.

 

4) Otherwise, if the type of the operand with signed integer type can represent

all of the values of the type of the operand with unsigned integer type, then

the operand with unsigned integer type is converted to the type of the

operand with signed integer type.

5) Otherwise, both operands are converted to the unsigned integer type

corresponding to the type of the operand with signed integer type.

 

Δες τώρα το παρακάτω κλασικό παράδειγμα:

 

>
int i = -5;
unsigned int u = 5;

if (i < u)
   printf("ama tupo8w na me ftuseis\n");

 

Σύμφωνα με τον κανόνα 3 στην έκφραση i < u, ο i θα μετατραπεί σε unsigned και επειδή το -5 δεν μπορεί να αναπαραστεί σε unsigned θα γίνει 4 εκατομμύρια κάτι οπότε δεν είναι μικρότερο του 5 :)

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

imitheos..

 

Μόλις ανέβηκες level στο ranking μου της nerdοσύνης (για καλό το λέω).

 

Ωραίος.

 

Θες να πεις ότι εσείς δεν έχετε κολλημένο στο γραφείο ένα χαρτάκι με τα promotions και άλλο με operator precedence ? :)

 

>
long i = -5;
unsigned int u = 5;

if (i < u)
   printf("8a tupo8w arage ?\n");

 

Πριν ξέχασα να γράψω αυτήν την έκδοση του προγράμματος που είναι πιο αστεία :) Γιατί είναι πιο αστεία ? Με 32bit int και long (ένα τυπικό 32bit σύστημα) ισχύει ο κανόνας 5 και οι τύποι μετατρέπονται σε unsigned οπότε ισχύει ό,τι και πριν και το μήνυμα δεν τυπώνεται. Με 64bit long και 32bit int (ένα τυπικό 64bit σύστημα εκτός από τα windows) μας πιάνει ο κανόνας 4 μια και ο long είναι μεγαλύτερος και μπορεί να αναπαραστήσει όλες τις τιμές ενός unsigned int. Έτσι οι δύο τύποι μετατρέπονται σε long οπότε το μήνυμα τυπώνεται :)

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

Θες να πεις ότι εσείς δεν έχετε κολλημένο στο γραφείο ένα χαρτάκι με τα promotions και άλλο με operator precedence ? :)

...

Εγώ όχι! Συνήθως φροντίζω να αποφεύγω όπου και όπως μπορώ τα implict conversions, προσπαθώντας να μην μπλέκω τύπους στις ίδιες πράξεις. Τις υπόλοιπες φορές κάνω explicit casting (συνήθως μεταξύ signed/unsigned ).

 

Επίσης συνήθως αφήνω τα prototypes να κάνουν τη δουλειά τους χωρίς να κατσάρω, όπως π.χ. στο...

 

>
char c = toupper(c);

 

αντί για...

 

>
char c = (char) topper( (int)c );

 

ή αντί π.χ. για...

>
int c;
printf( "%c", (char)toupper(c) );

 

Τώρα για ειδικά projects, κοιτάω σε links όπως αυτό που έδωσα παραπάνω, που αναφέρει όσο έχεις παραθέσει στο ποστ σου (και παραπάνω).

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

Τώρα για ειδικά projects, κοιτάω σε links όπως αυτό που έδωσα παραπάνω, που αναφέρει όσο έχεις παραθέσει στο ποστ σου (και παραπάνω).

 

Αν εννοείς το securecoding.cert δεν χαίρει και πολύ καλής φήμης. Είναι καλή ιδέα να μαζέψουν τις κακές πρακτικές και μπράβο τους για την προσπάθεια αλλά πολύς κόσμος τους κράζει γιατί ακόμη και μετά από τις τόσες διορθώσεις που έχουν γίνει (το κάθε post έχει περάσει από κάμποσες revisions), ακόμη και τώρα έχει λάθη.

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

So very true!

 

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

 

EDIT:

 

Μιας που πιάσαμε τα οverflows, μπορεί κάποια ευγενική ψυχή να μου εξηγήσει γιατί στην 32μπιτη έκδοση της Pelles-C η παρακάτω συνάρτηση κάνει wrap around την τιμή επιστροφής της ftell(), άμα της περαστεί αρχείο μεγέθους μεγαλύτερου του LONG_MAX?

 

>
/*********************************************************//**
* @brief	Return the size of a file in bytes, or 0 on error
*		(cannot be more than LONG_MAX).
*************************************************************
*/
uintmax_t f_size( const char *fname )
{
long int size;
FILE 	*fp;

if ( NULL == (fp = fopen(fname, "rb")) )	/* binary mode */
	return 0;

if ( 0 != fseek(fp, 0, SEEK_END) ) {
	fclose(fp);
	return 0;
}

size = ftell(fp);
fclose(fp);

return (size < 0) ? 0 : (uintmax_t)size;
}

Δεν υποτίθεται πως η ftell() επιστρέφει -1 σε περίπτωση overflow?

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

Χιλια ευχαριστώ χακερόνια μου!!!!!!!!!!!!!!!

 

Και ενα μεγαλο επισης φχαριστώ και στον Ημιθεο (το εύ κόπηκε λογω υπερχείλισης μπουχαχαχα)

 

Αυτο που μου εξηγησες στον κανονα 3 το ειχα σημειωσει και εγω σε μια προχειρη Α4 και το βρηκα τωρα προ λιγου

και θυμηθηκα οτι τελικα εψαχνα καποιον να επιβεβαιωσουμε αν εχω καταλαβει σωστα τον κανονα ! Νταξει αφου τον καταλαβαμε

αστον... στα αζήτητα τωρα !!!! Με πιασε πονοκεφαλος. Δεν παει...

 

Mιας και σας βρήκα ομως μάχιμους θα κάνω μια λιγοτερο confused ερωτηση σχετικα με την scanf (μιγφ1 μην το διαβασεις γιατι θα μου ριξεις καναν εξάψαλμο αλλα εχω μια ερωτηση δεν γινεται θα τη γραψω)

 

Έστω ο κώδικας :

 

>

do 
   { 
       printf("Give a character: \n");
       scanf("%c" , &ch);
       printf("%c" , ch);
   } while( ch!='q');

 

Λοιπον... να επισημανω πως γνωριζω αρκετα καλα πλεον το "προβλημα" της scanf και το βαζω σε εισαγωγικα γιατι δεν ειναι ακριβως δικο της προβλημα αλλα του line buffering. Δεν μπορω να καταλαβω το εξης... αν εγω βγαλω την 1η printf τοτε ο κωδικας παιζει μια χαρα... αν την βαλω οπως στο παραπανω μου αρχιζει τα νουμερα.... η ερωτηση δεν ειναι γιατι μου αρχιζει τα νουμερα η ερωτηση ειναι γιατι δεν μου αρχιζει τα νουμερα και οταν ΔΕΝ υπαρχει η printf.... τι αλλαζει? Ο βροχος συνεχιζει να εκτελειται οσο η μεταβλητη χαρακτήρα δεν ειναι q και αρα η συνθηκη δεν ειναι FALSE , αφου καθε φορα παιρνει τον '\n' σαν εισοδος η επομενη κληση της συνάρτησης.

 

Κανεις καμια ιδεα ? Τελειως απο περιεργεια...

Πιστευω γνωριζω αρκετα καλα πως δουλευει αυτο το loop αλλα δεν μπορω να εξηγησω αυτη τη συμπεριφορα...

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

So very true!

 

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

 

EDIT:

 

Μιας που πιάσαμε τα οverflows, μπορεί κάποια ευγενική ψυχή να μου εξηγήσει γιατί στην 32μπιτη έκδοση της Pelles-C η παρακάτω συνάρτηση κάνει wrap around την τιμή επιστροφής της ftell(), άμα της περαστεί αρχείο μεγέθους μεγαλύτερου του LONG_MAX?

 

>
/*********************************************************//**
* @brief	Return the size of a file in bytes, or 0 on error
*		(cannot be more than LONG_MAX).
*************************************************************
*/
uintmax_t f_size( const char *fname )
{
long int size;
FILE 	*fp;

if ( NULL == (fp = fopen(fname, "rb")) )	/* binary mode */
	return 0;

if ( 0 != fseek(fp, 0, SEEK_END) ) {
	fclose(fp);
	return 0;
}

size = ftell(fp);
fclose(fp);

return (size < 0) ? 0 : (uintmax_t)size;
}

Δεν υποτίθεται πως η ftell() επιστρέφει -1 σε περίπτωση overflow?

Το μόνο σίγουρο για την ftell είναι ότι επιστρέφει -1L σε περίπτωση αποτυχίας και θέτει το errno σε EBADF ή ESPIPE.

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

Το μόνο σίγουρο για την ftell είναι ότι επιστρέφει -1L σε περίπτωση αποτυχίας και θέτει το errno σε EBADF ή ESPIPE.

 

Το οποίο δεν πρέπει να "πιαστεί" στο return της συνάρτησης (και να επιστρέψει 0) ;

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

Το οποίο δεν πρέπει να "πιαστεί" στο return της συνάρτησης (και να επιστρέψει 0) ;

Αυτό είναι το αναμενόμενο..

 

* Θα δω από περιέργεια πως αντιδρά σε C++ Builder η ftell στην περίπτωση που περιγράφεις με ένα αρχείο πάνω από 2GB και θα αναρτήσω αποτελέσματα .. *

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

Έχω φρικάρει φίλε DirectX με το συγκεκριμένο... ο 32μπιτος gcc του mingw λειτουργει κανονικά. Αργότερα που θα πάω σπίτι (εκεί έχω την 32μπιτη Pelles, εδώ έχω την 64μπιτη, που λειτουργεί σωστά) θα προσθέσω έναν εξτρα έλεγχο για errno != 0 στην f_size ...

 

>
return (errno != 0 || size < 0) ? 0 : (uintmax_t)size;

ψυλλιάζομαι πως έχει bug η 32μπιτη Pelles με την ftell()

 

...

Mιας και σας βρήκα ομως μάχιμους θα κάνω μια λιγοτερο confused ερωτηση σχετικα με την scanf (μιγφ1 μην το διαβασεις γιατι θα μου ριξεις καναν εξάψαλμο αλλα εχω μια ερωτηση δεν γινεται θα τη γραψω)

 

Έστω ο κώδικας :

 

>

do 
   { 
       printf("Give a character: \n");
       scanf("%c" , &ch);
       printf("%c" , ch);
   } while( ch!='q');

 

Λοιπον... να επισημανω πως γνωριζω αρκετα καλα πλεον το "προβλημα" της scanf και το βαζω σε εισαγωγικα γιατι δεν ειναι ακριβως δικο της προβλημα αλλα του line buffering. Δεν μπορω να καταλαβω το εξης... αν εγω βγαλω την 1η printf τοτε ο κωδικας παιζει μια χαρα... αν την βαλω οπως στο παραπανω μου αρχιζει τα νουμερα.... η ερωτηση δεν ειναι γιατι μου αρχιζει τα νουμερα η ερωτηση ειναι γιατι δεν μου αρχιζει τα νουμερα και οταν ΔΕΝ υπαρχει η printf.... τι αλλαζει? Ο βροχος συνεχιζει να εκτελειται οσο η μεταβλητη χαρακτήρα δεν ειναι q και αρα η συνθηκη δεν ειναι FALSE , αφου καθε φορα παιρνει τον '\n' σαν εισοδος η επομενη κληση της συνάρτησης.

 

Κανεις καμια ιδεα ? Τελειως απο περιεργεια...

Πιστευω γνωριζω αρκετα καλα πως δουλευει αυτο το loop αλλα δεν μπορω να εξηγησω αυτη τη συμπεριφορα...

Υπάρχει και χωρίς την printf το πρόβλημα, δοκίμασε τον κώδικα με το 2ο printf αλλαγμένο σε...

 

>    	printf(">>>%c" , ch);

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

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

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