imitheos Δημοσ. 19 Μαΐου 2013 Δημοσ. 19 Μαΐου 2013 Καλησπέρα. Είδα πριν λίγο κάτι που με παραξένεψε σχετικά με την sizeof. Δεν είναι κάτι σημαντικό ώστε να αξίζει ξεχωριστό νήμα αλλά μια και το νήμα των ερωτήσεων κλείδωσε αναγκαστικά πρέπει να ανοίξω νέο. Αν κάποια στιγμή ξεκλειδωθεί παρακαλώ να μεταφερθεί εκεί. Δείτε λίγο αυτό το commit του stable πυρήνα και συγκεκριμένα το παρακάτω κομμάτι. const char *temp = "temporary "; if (strncmp(buf, temp, sizeof(temp) - 1) == 0) { buf += sizeof(temp) - 1; sdkp->cache_override = 1; } else { sdkp->cache_override = 0; } Ας υποθέσουμε ότι στο buf μπορούμε να έχουμε είτε το string "write through" είτε το string "temporary write through". O κώδικας ελέγχει αν υπάρχει η λέξη temporary και αν υπάρχει ενεργοποιεί μια επιλογή και επίσης πηδάει χαρακτήρες ώστε το string να δείχνει στο "write through". Σωστά ? Έχω πολύ πονοκέφαλο και σε συνδυασμό με το γεγονός ότι έχει sign-off από 3 kernel devs μου δίνει την εντύπωση ότι σίγουρα λέω μ..κίες αλλά με παραξένεψε η χρήση της sizeof. Αν είχαμε char temp[] = "temporary " θα καταλάβαινα τη χρήση του sizeof αλλά εφόσον έχουμε *temp δεν θα επιστρέψει το μέγεθος του δείκτη ? Δεν θα έπρεπε να χρησιμοποιηθεί strlen και επίσης να μην υπάρχουν τα "-1" στις δύο εκφράσεις ?
temp_ Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Το ίδιο βλέπω κι εγώ, οπότε είμαστε 2 insomnia members vs 3 kernel devs (ακόμα αποτελούμε μειοψηφία πάντως). Το -1 όμως τα βλέπω οκ, γιατί δεν θέλουν να συμμετάσχει το '\0' στην σύγκρισή (μιας και στο buf δεν αναμένεται να υπάρχει '\0' μετά το "temporary").
cvb Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Καλημέρα. Πήρα το μικρό τμήμα κώδικα: if (strncmp(buf, temp, sizeof(temp) - 1) == 0) { buf += sizeof(temp) - 1; και το έβαλα σε ένα νέο πρόγραμμα για να δω την συμπεριφορά του: #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { const char *temp = "temporary "; char buf[] = "temporary "; if (strncmp(buf, temp, sizeof(temp) - 1) == 0) { // buf += sizeof(temp) - 1; // error C2106: '+=' : left operand must be l-value printf( "sizeof(buf) %d sizeof(temp) %d strlen(buf) %d strlen(temp) %d\n", sizeof(buf), sizeof(temp), strlen(buf), strlen(temp) ); } system("PAUSE"); } Βγάζει λάθος C2106 και δεν μπορώ να σκεφτώ ένα σωστό τρόπο να λειτουργήσει. Η printf() είναι λίγο περιττή, την έβαλα για να εμφανίσει το μεγέθος και το μήκος του *temp και του buf[]. Σχετικά με το -1 ίσως θα μπορούσε να έχει μια #define, αλλά νομίζω όταν πρόκειται για το '\0' συνηθήζετε πιο πολύ να χρησιμοποιήτε απευθείας. Καλή συνέχεια.
imitheos Δημοσ. 20 Μαΐου 2013 Μέλος Δημοσ. 20 Μαΐου 2013 Το ίδιο βλέπω κι εγώ, οπότε είμαστε 2 insomnia members vs 3 kernel devs (ακόμα αποτελούμε μειοψηφία πάντως). Το -1 όμως τα βλέπω οκ, γιατί δεν θέλουν να συμμετάσχει το '\0' στην σύγκρισή (μιας και στο buf δεν αναμένεται να υπάρχει '\0' μετά το "temporary"). Άντε άλλοι δύο και τους φάγαμε Ναι έχεις δίκιο για το -1 γιατί το sizeof θα επιστρέψει και το \0. Εγώ είχα στο μυαλό μου την μορφή με το strlen που δεν θα έπρεπε να υπάρχει. Άρα όλα δείχνουν ότι ήθελαν να γράψουν const char temp[] Έστειλα e-mail και περιμένω απάντηση. Θα σας πω αν έγινα ρεζίλι ή όχι
Directx Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Άρα όλα δείχνουν ότι ήθελαν να γράψουν const char temp[] Yup, διαφορετικά το sizeof επιστρέφει το μέγεθος του pointer όποτε..
temp_ Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Καλημέρα. Πήρα το μικρό τμήμα κώδικα: if (strncmp(buf, temp, sizeof(temp) - 1) == 0) { buf += sizeof(temp) - 1; και το έβαλα σε ένα νέο πρόγραμμα για να δω την συμπεριφορά του: #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { const char *temp = "temporary "; char buf[] = "temporary "; if (strncmp(buf, temp, sizeof(temp) - 1) == 0) { // buf += sizeof(temp) - 1; // error C2106: '+=' : left operand must be l-value printf( "sizeof(buf) %d sizeof(temp) %d strlen(buf) %d strlen(temp) %d\n", sizeof(buf), sizeof(temp), strlen(buf), strlen(temp) ); } ... } Βγάζει λάθος C2106 και δεν μπορώ να σκεφτώ ένα σωστό τρόπο να λειτουργήσει.... Αν αλλάξεις τον ορισμό του buf σε: char *buf = "temporary"; ή σε const char *buf = "temporary"; θα λειτουργήσει. Όταν το ορίζεις ως πίνακα, τότε ο δείκτης που δείχνει στο 1ο του στοιχείο δεν είναι lvalue, άρα η γραμμή... buf += sizeof(temp) - 1; χτυπάει λόγω της απόπειρας ανάθεσης. Αλλά για να βγάλει και σωστό αποτέλεσμα ο όλος κώδικας θα πρέπει να αλλάξεις και τον ορισμό του temp σε: char temp[] = "temporary"; ή σε const char temp[] = "temporary"; Όταν το ορίζεις ως δείκτη (αντί για πίνακα) τότε είναι lvalue, αλλά το sizeof(temp) απεικονίζει πλέον το μέγεθος του δείκτη και όχι το μέγεθος όλων των στοιχείων του δυναμικού πίνακα στον οποίον δείχνει ο temp.
bird Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει; Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι...
temp_ Δημοσ. 20 Μαΐου 2013 Δημοσ. 20 Μαΐου 2013 Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει; Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι... Βασικά δεν έχει λογική να κάνει κάτι τέτοιο ο compiler. Μια γρήγορη δοκιμή σε 4 διαφορετικούς compilers δείχνει πως όντως δεν το κάνει... ... const char *cp1 = "temporary "; char *cp2 = "temporary "; const char s1[] = "temporary "; char s2[] = "temporary "; printf( "%lu, %lu, %lu, %lu\n", sizeof(cp1), sizeof(cp2), sizeof(s1), sizeof(s2) ); ... /* Έξοδος: 4, 4, 11, 11 */
imitheos Δημοσ. 20 Μαΐου 2013 Μέλος Δημοσ. 20 Μαΐου 2013 Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει; Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι... Και εγώ το σκέφτηκα χτες και ενώ μάλιστα ήξερα ότι αποκλείεται να συμβαίνει, παρόλα αυτά όμως το έτρεξα χτες για να δω τι θα βγει Μην ξεχνάς ότι το sizeof αναφέρεται στο αντικείμενο temp που έχει μέγεθος δείκτη και επίσης το const δεν έχει ιδιαίτερη σημασία στην C. #include <stdio.h> #include <string.h> int main(void) { const char *temp = "temporary "; printf("sizeof=%zu - strlen=%zu\n", sizeof(temp), strlen(temp)); return 0; } Όπως ήταν αναμενόμενο πήρα το παρακάτω αποτέλεσμα: % gcc -Wall -o 64 tmp.c % gcc -Wall -o 32 -m32 tmp.c % ./64;./32 sizeof=8 - strlen=10 sizeof=4 - strlen=10 Μάλλον από κεκτημένη ταχύτητα έγραψε *temp ενώ ήθελε temp[].
imitheos Δημοσ. 21 Μαΐου 2013 Μέλος Δημοσ. 21 Μαΐου 2013 Απάντησαν τελικά στο e-mail; Βιάζεσαι Αν δεν παραπέσει το mail μέσα στο haystack σε κανένα διήμερο λογικά. Άσε που μπορεί να μην στείλει καν. Για τόσο trivial θέματα μερικοί απλά το διορθώνουν χωρίς να απαντήσουν.
cvb Δημοσ. 21 Μαΐου 2013 Δημοσ. 21 Μαΐου 2013 Καλημέρα. Στο παρακάτω πρόγραμμα στην δεύτερη δήλωση της συνάρτησης printf() περίμενα να εμφανίσει 4.000000 και όχι 0.0000000 και αναρωτιέμαι γιατί συμβαίνει αυτό. Με το σκεπτικό ότι και η ίδια η sizeof() καταναλώνει κάποιο μέγεθος μνήμης. Μπορεί να σκέφτομαι λάθος. #include <stdio.h>#include <stdlib.h>int main(void){ printf( "sizeof( sizeof(NULL) ): (decimal) %d\n", sizeof( sizeof(NULL) ) ); printf( "sizeof( sizeof(NULL) ): (float) %f\n", sizeof( sizeof(NULL) ) ); system( "PAUSE" ); return 0;} Η έξοδος του προγράμματος είναι: sizeof( sizeof(NULL) ): (decimal) 4sizeof( sizeof(NULL) ): (float) 0.000000Press any key to continue . . . Καλή συνέχεια.
imitheos Δημοσ. 21 Μαΐου 2013 Μέλος Δημοσ. 21 Μαΐου 2013 Καλημέρα. Στο παρακάτω πρόγραμμα στην δεύτερη δήλωση της συνάρτησης printf() περίμενα να εμφανίσει 4.000000 και όχι 0.0000000 και αναρωτιέμαι γιατί συμβαίνει αυτό. Με το σκεπτικό ότι και η ίδια η sizeof() καταναλώνει κάποιο μέγεθος μνήμης. Μπορεί να σκέφτομαι λάθος.Παρόλο που χρησιμοποιούμε παρενθέσεις, ο sizeof είναι τελεστής και όχι συνάρτηση (για αυτό και μπορεί να ξέρει το μέγεθος κατά το compile). Φυσικά εννοείται πως δεν καταναλώνει μνήμη. printf( "sizeof( sizeof(NULL) ): (decimal) %d\n", sizeof( sizeof(NULL) ) ); printf( "sizeof( sizeof(NULL) ): (float) %f\n", sizeof( sizeof(NULL) ) ); Η έξοδος του προγράμματος είναι: sizeof( sizeof(NULL) ): (decimal) 4 sizeof( sizeof(NULL) ): (float) 0.000000 Press any key to continue . . . Καλή συνέχεια. Ας δούμε καταρχάς τι μηνύματα εμφανίζει ο compiler: 32bit: προειδοποίηση: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘unsigned int’ [-Wformat=] 64bit: προειδοποίηση: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=] 64bit: προειδοποίηση: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘long unsigned int’ [-Wformat=] Τι μας λέει εδώ ? Ότι ενώ το printf χρειάζεται double, εσύ του έδωσες unsigned int. Σε 64bit βαράει και το 1ο printf γιατί ζητάς να εμφανίσει %d και του έδωσες long unsigned int. Έπρεπε δηλαδή να έχεις %ld σε 64bit. Χάριν συντομίας ας δούμε την 32bit περίπτωση. Το NULL (δηλαδή το (void *)0) είναι δείκτης οπότε το sizeof του είναι 4. Ως εδώ είμαστε οκ. Τι κάνεις όμως εσύ τώρα ? Του λες να σου βρει το μέγεθος του αριθμού 4. Αν είχες γράψει χειροκίνητα sizeof(4) θα έπαιρνες το μέγεθος ενός int γιατί το κάθε literal που χωράει σε int έχει τύπο int (προφανώς το 4 χωράει σε int). Εν προκειμένω όμως το 2ο sizeof βλέπει το αποτέλεσμα του 1ου sizeof. Ο sizeof τελεστής επιστρέφει πάντα τύπο size_t έτσι αυτό που έγραψες είναι σαν να είχες γράψει sizeof((size_t)4) για αυτό και παίρνεις το μέγεθος του τύπου size_t. ** Μερικοί οδηγοί λένε ότι το δεύτερο sizeof είναι σαν να μην υπάρχει και παίρνεις το μέγεθος του δείκτη. Αυτό _δεν_ ισχύει. Αυτή η άποψη γεννήθηκε από το γεγονός ότι συμπτωματικά στις περισσότερες πλατφόρμες το μέγεθος αυτού του τύπου είναι ίδιο με το μέγεθος του δείκτη για αυτό και σε 32bit παίρνεις 4 και σε 64bit παίρνεις 8. Δεν είναι παντού όμως έτσι και μπορεί κάλλιστα το sizeof(sizeof(NULL)) να είναι διαφορετικό του sizeof(NULL) ** Ας δούμε τώρα την περίπτωση με το %f. Η έκφραση που έχεις είναι ίδια με πριν οπότε το αποτέλεσμα θα είναι πάλι είτε 4 ή 8 αλλά αυτή τη φορά είπες στο printf να σου εμφανίσει ένα δεκαδικό αριθμό ενώ του έδωσες ακέραιο (όπως σου λέει και ο compiler). Το πρότυπο λέει πως όταν δεν συμβαδίζει το format που ζητείται με αυτό που δίνεται, τότε η συμπεριφορά είναι αόριστη. Μπορεί να τυπώσει 42 ή μπορεί να εμφανίσει ένα 3d χέρι που να σε μουτζώνει. Μερικές φορές η υλοποίηση θα εμφανίσει τον αριθμό που έδωσες σαν να ήταν δεκαδικός (δηλαδή το bit pattern του σε κάποια αναπαράσταση όπως IEEE-754). Γιατί τώρα βγαίνει 0.000 ? Γιατί ο αριθμός 4 και 8 έχουν μηδενικά τα bit του exponent οπότε βγαίνει ένας πολύ μικρός αριθμός. Αν αλλάξεις το %f σε %g ώστε να σου εμφανίσει την επιστημονική μορφή του, τότε θα σου εμφανίσει κάτι. union tmp { float f; int i; } u; u.i = 8; printf("i=%d - f=%f - f=%g\n", u.i, u.f, u.f); Έξοδος: i=8 - f=0.000000 - f=1.12104e-44 Όπως βλέπεις, ο αριθμός 8 αν τον θεωρήσουμε double στη δική μου πλατφόρμα με IEEE-754 αναπαράσταση αντιστοιχεί στον αριθμό 1.12104e-44 δηλαδή ένα πάρα πολύ μικρό αριθμό ο οποίος αναγκαστικά εμφανίζεται ως 0.0. Η union μπήκε ώστε να αναγκάσω τον compiler (αν είναι συμβατός με >= C99-TC3) να εμφανίσει το bit pattern σωστά. Αν είχα σκέτο printf("%f\n", 4); μπορεί όπως είπαμε να εμφάνιζε 42. 2
παπι Δημοσ. 21 Μαΐου 2013 Δημοσ. 21 Μαΐου 2013 Μπας και εχει αλλη συμπεριφορα οταν γραφουν σε kernel mode; Οπως πχ με τους registers που σε kernel mode ειναι ολοκληρη ενω σε user mode ειναι μιση
cvb Δημοσ. 23 Μαΐου 2013 Δημοσ. 23 Μαΐου 2013 Ευχαριστώ πολύ για την απάντηση. Το ίδιο και τον χρήστη temp_ για την απάντηση του στο προηγούμενο θέμα. Καλή συνέχεια.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα