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

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

Δημοσ.

Καλησπέρα.

 

Είδα πριν λίγο κάτι που με παραξένεψε σχετικά με την 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" στις δύο εκφράσεις ?

Δημοσ.

Το ίδιο βλέπω κι εγώ, οπότε είμαστε 2 insomnia members vs 3 kernel devs (ακόμα αποτελούμε μειοψηφία πάντως).

 

Το -1 όμως τα βλέπω οκ, γιατί δεν θέλουν να συμμετάσχει το '\0' στην σύγκρισή (μιας και στο buf δεν αναμένεται να υπάρχει '\0' μετά το "temporary").

Δημοσ.

Καλημέρα.

Πήρα το μικρό τμήμα κώδικα:

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' συνηθήζετε πιο πολύ να χρησιμοποιήτε απευθείας.

Καλή συνέχεια.

Δημοσ.

Το ίδιο βλέπω κι εγώ, οπότε είμαστε 2 insomnia members vs 3 kernel devs (ακόμα αποτελούμε μειοψηφία πάντως).

 

Το -1 όμως τα βλέπω οκ, γιατί δεν θέλουν να συμμετάσχει το '\0' στην σύγκρισή (μιας και στο buf δεν αναμένεται να υπάρχει '\0' μετά το "temporary").

Άντε άλλοι δύο και τους φάγαμε :)

 

Ναι έχεις δίκιο για το -1 γιατί το sizeof θα επιστρέψει και το \0. Εγώ είχα στο μυαλό μου την μορφή με το strlen που δεν θα έπρεπε να υπάρχει. Άρα όλα δείχνουν ότι ήθελαν να γράψουν const char temp[]

 

Έστειλα e-mail και περιμένω απάντηση. Θα σας πω αν έγινα ρεζίλι ή όχι :P

Δημοσ.

Άρα όλα δείχνουν ότι ήθελαν να γράψουν const char temp[]

 

Yup, διαφορετικά το sizeof επιστρέφει το μέγεθος του pointer όποτε..

Δημοσ.

 

Καλημέρα.

Πήρα το μικρό τμήμα κώδικα:

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.

Δημοσ.

Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει;

Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι... :P

Δημοσ.

Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει;

Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι... :P

 

Βασικά δεν έχει λογική να κάνει κάτι τέτοιο ο 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
*/
Δημοσ.

Μήπως το ότι ο temp είναι const να "αναγκάζει" τον compiler να το μεταφράσει σαν temp[] αφού έτσι κι αλλιώς δεν πρόκειται να αλλάξει;

Μπορεί και να λέω βλακεία αλλά μια που το σκέφτηκα, τσάμπα είναι... :P

Και εγώ το σκέφτηκα χτες και ενώ μάλιστα ήξερα ότι αποκλείεται να συμβαίνει, παρόλα αυτά όμως το έτρεξα χτες για να δω τι θα βγει :) Μην ξεχνάς ότι το 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[].
Δημοσ.

Απάντησαν τελικά στο e-mail;

Βιάζεσαι :P Αν δεν παραπέσει το mail μέσα στο haystack σε κανένα διήμερο λογικά. Άσε που μπορεί να μην στείλει καν. Για τόσο trivial θέματα μερικοί απλά το διορθώνουν χωρίς να απαντήσουν.

Δημοσ.

Καλημέρα. Στο παρακάτω πρόγραμμα στην δεύτερη δήλωση της συνάρτησης 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) 4
sizeof( sizeof(NULL) ): (float)   0.000000
Press any key to continue . . .

 

 

Καλή συνέχεια.

Δημοσ.

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

  • Like 2
Δημοσ.

Μπας και εχει αλλη συμπεριφορα οταν γραφουν σε kernel mode; Οπως πχ με τους registers που σε kernel mode ειναι ολοκληρη ενω σε user mode ειναι μιση

Δημοσ.

Ευχαριστώ πολύ για την απάντηση. Το ίδιο και τον χρήστη temp_ για την απάντηση του στο προηγούμενο θέμα. Καλή συνέχεια.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...