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

malloc & free


sonyxp

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

Προσωπικά έχω συναντήσει πάμπολλες φορές κώδικες με την παραπάνω πρακτική και να βασίζονται κατόπιν στο ότι η memset( , 0, ) θέτει τους δείκτες σε NULL, κάτι που μπορεί να (θα) δημιουργήσει προβλήματα αν μεταφερθεί σε compilers με non-0-bits implementation.

Και εγώ το έχω συναντήσει αλλά το θεωρούσα λάθος πάντα ακόμη και σε x86 (Το σκεπτικό μου συνεχίζεται στο επόμενο quote)

 

 

Το πρότυπο δεν το εγγυάται, πλην όμως παίζει κανονικά σε όλα τα 0-bits implementations, που είναι και η τυπική υλοποίηση στους δημοφιλείς compilers.

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

 

Η memset θέτει μεν την αριθμητική τιμή 0 όμως το κάνει μέσω μεταβλητής οπότε δεν δημιουργεί null pointer (ακόμη και μέσω σταθεράς να έθετε το 0 δεν γνωρίζει σε τι τύπο αναφέρεται ο δείκτης που της έχει δοθεί για να θέσει σωστά την τιμή). Άρα η έκφραση "if (foo.ptr == 0)" πρέπει να αποτύχει (επειδή έχουμε ένα κανονικό δείκτη με τιμή 0 και κανένας κανονικός δείκτης δεν μπορεί να ισούται με null δείκτη).

 

Ο κώδικάς σου όμως λειτουργεί όπως παρουσίασες και σε Clang οπότε ή στη περίπτωση του x86 οι devs δεν θεώρησαν απαραίτητο τον κόπο για να υλοποιήσουν το "δείκτης με 0 != null δείκτης" ή (πιο πιθανό) παρερμήνευσα το πρότυπο.

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

  • Απαντ. 63
  • Δημ.
  • Τελ. απάντηση

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

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

imitheos είσαι σχεδόν θεός και χαίρομαι που ποστάρεις εισάγοντας στην κουβέντα ενδιαφέρουσες λεπτομέρειες. Για παράδειγμα, με έβαλες να σκεφτώ ότι εφόσον π.χ. ο gcc ρίχνει άκυρο στη memset αυτό θα μπορούσε να δικαιολογείται μόνο βάσει του ότι το deref σε freed pointer είναι UB και του κανόνα as-if. 

 

Το οποίο με τη σειρά του σημαίνει πως για τον ίδιο ακριβώς λόγο αυτό που έγραψα νωρίτερα

Άρα, τα περιεχόμενα της μνήμης πρέπει να παραμένουν απείραχτα γιατί ο ορισμός του τι πρέπει να κάνει η free δεν αναφέρει τίποτα σχετικά μ' αυτά.

είναι λάθος (UB is UB, άρα δεν "πρέπει" τίποτα).

 

Στο δια ταύτα τώρα, βαριέμαι να ανοίξω το πρότυπο αλλά γιατί θεωρείς πως είναι guaranteed ότι το all zero bits σε pointer ποτέ δε θα πρέπει να τον κάνει null pointer? Εγώ έχω στο μυαλό μου πως είναι implementation-defined το τι θα γίνει.

 

Link time:

 

1) Stack overflow

2) Φαντάζομαι πως θα βρείτε ενδιαφέρον αυτό. Η ιστορία που λέει για το Win32s ναι μεν δε θα επηρρέαζε το ότι αν έγραφες all zero bits σε ένα pointer θα εξακολουθούσε να συμπεριφέρεται σα null pointer, αλλά μπορώ να θεωρητικά να φανταστώ έναν π.χ. real mode DOS compiler να κάνει το ίδιο πράγμα σε υψηλότερο επίπεδο (απλά δηλαδή να παράγει κώδικα που κάνει αριθμητική στους pointers πριν τον κώδικα που υλοποιεί το όποιο σχετικό dereference) προκειμένου να πετύχει τον ίδιο σκοπό: ένα γράψιμο στο null pointer να μη στέλνει το σύστημα αδιάβαστο.

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

Για παράδειγμα, με έβαλες να σκεφτώ ότι εφόσον π.χ. ο gcc ρίχνει άκυρο στη memset αυτό θα μπορούσε να δικαιολογείται μόνο βάσει του ότι το deref σε freed pointer είναι UB και του κανόνα as-if.

Εφόσον η memset τρέχει πριν το free γίνεται deref σε freed pointer ? Εγώ είχα την εντύπωση ότι αφαιρεί την memset επειδή βλέπει ότι δεν χρησιμοποιείται μετά η μεταβλητή (κάτι σαν τα warnings "τάδε variable set but not used").

 

Στο δια ταύτα τώρα, βαριέμαι να ανοίξω το πρότυπο αλλά γιατί θεωρείς πως είναι guaranteed ότι το all zero bits σε pointer ποτέ δε θα πρέπει να τον κάνει null pointer? Εγώ έχω στο μυαλό μου πως είναι implementation-defined το τι θα γίνει.

Ναι μπορεί να αφήνεται στην υλοποίηση το πως θα γίνει και να μην ερμήνευσα σωστά το πρότυπο(τουλάχιστον η συμπεριφορά του κώδικα του migf1 σε gcc και clang αυτό δείχνουν).

 

Το τμήμα που παρέθεσα και στο άλλο μήνυμά μου είναι το εξής:

An integer constant expression with the value 0, or such an expression cast to type

void *, is called a null pointer constant.55) If a null pointer constant is converted to a

pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal

to a pointer to any object or function.

Από αυτό το τμήμα συμπεραίνω δύο πράγματα. Αφενός ότι ένας null δείκτης δεν μπορεί να ισούται με ένα κανονικό δείκτη και αφετέρου ότι null δείκτη μπορείς να δημιουργήσεις μόνο με ένα "null pointer constant" δηλαδή με την έκφραση του 0 ως σταθερά. Άρα το if θα έπρεπε να αποτυγχάνει γιατί συγκρίνει ένα null δείκτη (το 0 δεξιά του == τελεστή) με ένα κανονικό δείκτη (η μεταβλητή με το all-bits-zero).

 

Βέβαια τώρα που το ξαναδιαβάζω, η σύνταξη της πρόταξης είναι "αν ένα null pointer constant χρησιμοποιηθεί σε ένα δείκτη, τότε το αποτέλεσμα είναι null δείκτης" δηλαδή δεν αναφέρει ρητά το "μόνο" που είπα εγώ. Έτσι ίσως το all-bits-zero να αφήνεται στην υλοποίηση όπως είπες.

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

Εφόσον η memset τρέχει πριν το free γίνεται deref σε freed pointer ? Εγώ είχα την εντύπωση ότι αφαιρεί την memset επειδή βλέπει ότι δεν χρησιμοποιείται μετά η μεταβλητή (κάτι σαν τα warnings "τάδε variable set but not used").

 

Εννοώ πως επειδή αμέσως μετά τη memset υπάρχει η free, και επειδή δε μπαίνει στην κουβέντα τίποτα σχετικό με threading model, ο compiler μπορεί να αποδείξει πως η αλλαγή στα περιεχόμενα της μνήμης από τη memset μπορεί να είναι ορατή μόνο σε κώδικα που θα τρέξει μετά τη free. Αλλά για να γίνει ορατή αυτός ο κώδικας θα πρέπει να κάνει deref τον πριν λίγο freed pointer, οπότε UB. Άρα η memset μπορεί να εξαλειφθεί τελείως γιατί το να δεις άλλες τιμές από τα μηδενικά που περιμένεις λόγω της memset είναι φυσικά μέσα στα πλαίσια της UB.

 

Από αυτό το τμήμα συμπεραίνω δύο πράγματα. Αφενός ότι ένας null δείκτης δεν μπορεί να ισούται με ένα κανονικό δείκτη

 

"guaranteed to compare unequal to a pointer to any object or function".

 

Δεν ξέρω πώς το σκέφτεσαι οπότε για σιγουριά θα το πω: το bold έχει σημασία. Αυτό που σου λέει είναι στην ουσία πως ο compiler και το runtime (think malloc) είναι υποχρεωμένοι να μην αποθηκεύσουν κανένα object (μεταβλητή, allocated memory, κλπ) ή function σε διεύθυνση μνήμης τέτοια ούτως ώστε αυτή να ισούται αριθμητικά με την αναπαράσταση του null pointer (η οποία βέβαια μπορεί να είναι οποιαδήποτε).

 

Για παράδειγμα, σε μια υποτιθέμενη υλοποίηση η οποία λειτουργεί ως εξής (αυτό είναι 100% legal αν και στην πράξη δεν πρόκειται να συμβεί ποτέ):

void* p = 0; // null pointer
printf("%p", p); // "0xabad1dea"

ο compiler είναι υποχρεωμένος να μη βάλει τίποτα στη διεύθυνση 0xabad1dea και η malloc ποτέ δε μπορεί να σου επιστρέψει την ίδια τιμή.

 

Αυτό όμως δε σημαίνει ότι εσύ δε μπορείς να κάνεις το παρακάτω "με το έτσι θέλω":

 

void* p1 = 0;
void* p2 = 0xabad1dea;

if (p1 == p2) printf("equal!\n");

Εδώ θα γράψει equal επειδή ο p2 δεν είναι pointer σε object or function, είναι pointer που του έδωσες εσύ την τιμή που σου αρέσει. Προφανώς δεν είναι δυνατό να σε εμποδίσει ο compiler να κάνεις κάτι τέτοιο και το πρότυπο όπως είναι γραμμένο του επιτρέπει να γράψει equal.

 

και αφετέρου ότι null δείκτη μπορείς να δημιουργήσεις μόνο με ένα "null pointer constant" δηλαδή με την έκφραση του 0 ως σταθερά. Άρα το if θα έπρεπε να αποτυγχάνει γιατί συγκρίνει ένα null δείκτη (το 0 δεξιά του == τελεστή) με ένα κανονικό δείκτη (η μεταβλητή με το all-bits-zero).

 

Αν κατάλαβα καλά αυτά που έγραψα παραπάνω ισχύουν και εδώ.

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

Δημοσ. (επεξεργασμένο)

...

 

 

 

"guaranteed to compare unequal to a pointer to any object or function".

 

Δεν ξέρω πώς το σκέφτεσαι οπότε για σιγουριά θα το πω: το bold έχει σημασία. Αυτό που σου λέει είναι στην ουσία πως ο compiler και το runtime (think malloc) είναι υποχρεωμένοι να μην αποθηκεύσουν κανένα object (μεταβλητή, allocated memory, κλπ) ή function σε διεύθυνση μνήμης τέτοια ούτως ώστε αυτή να ισούται αριθμητικά με την αναπαράσταση του null pointer (η οποία βέβαια μπορεί να είναι οποιαδήποτε).

 

Για παράδειγμα, σε μια υποτιθέμενη υλοποίηση η οποία λειτουργεί ως εξής (αυτό είναι 100% legal αν και στην πράξη δεν πρόκειται να συμβεί ποτέ):

void* p = 0; // null pointer
printf("%p", p); // "0xabad1dea"
ο compiler είναι υποχρεωμένος να μη βάλει τίποτα στη διεύθυνση 0xabad1dea και η malloc ποτέ δε μπορεί να σου επιστρέψει την ίδια τιμή.

 

Αυτό όμως δε σημαίνει ότι εσύ δε μπορείς να κάνεις το παρακάτω "με το έτσι θέλω":

 

void* p1 = 0;
void* p2 = 0xabad1dea;

if (p1 == p2) printf("equal!\n");
Εδώ θα γράψει equal επειδή ο p2 δεν είναι pointer σε object or function, είναι pointer που του έδωσες εσύ την τιμή που σου αρέσει. Προφανώς δεν είναι δυνατό να σε εμποδίσει ο compiler να κάνεις κάτι τέτοιο και το πρότυπο όπως είναι γραμμένο του επιτρέπει να γράψει equal.

 

 

Αν κατάλαβα καλά αυτά που έγραψα παραπάνω ισχύουν και εδώ.

 

 

 

Καταρχήν, το ...

 

void* p2 = 0xabad1dea;

δεν περνάει χωρίς warning από κανέναν ANSI+ C compiler έχει πέσει στα χέρια μου. Για να περάσει πρέπει να  κάνεις cast το 0xabad1dea σε (void *) ή σε όποιο άλλο type ορίζεις τον p2.

 

Από την στιγμή λοιπόν που το κάνεις cast, ουσιαστικά το παίρνεις πάνω σου και λες του compiler να κάνει treat το 0xabad1dea ως διεύθυνση ενός object of type void (ή όποιον άλλον τύπο έχεις ορίσει για το p2).

 

Έπειτα, το ...

 

void* p1 = 0;

περνάει χωρίς warning όταν το NULL υλοποιείται με 0-bits, διότι δεν έχει κανένα νόημα να βγάλει warning, αλλά δεν είμαι καθόλου σίγουρος πως περνάει σε compilers που δεν υλοποιούν το NULL με 0-bits (έχουν περάσει πολλά χρόνια από την τελευταία φορά που χρειάστηκε να δουλέψω σε τέτοιες πλατφόρμες). Λογικά δεν περνάει (EDIT2: βλέπε http://www.insomnia.gr/topic/484331-malloc-free/page-3?do=findComment&comment=52328603), αλλά η ουσία είναι πως αν αντί για 0 χρησιμοποιείς NULL τότε περνάει πάντα & παντού και δουλεύει πάντα & παντού.

 

Ακόμα, το ...

 

printf("%p", p); // "0xabad1dea"

δεν είναι portable και ο κάθε compiler είναι ελεύθερος να σου τυπώσει ότι θέλει εκείνος όταν το p == NULL. Για παράδειγμα, ο lcc δεν σου τυπώνει τη διεύθυνση του NULL αλλά το string "(null)".

 

Αν θέλεις να βλέπεις το NULL implementation σε οποιονδήποτε compiler μπορείς να γράψεις μια μικρή portable συνάρτηση όπως η παρακάτω...

 

/* ------------------------------------------------- */
void print_ptr_address( void *p )
{
    size_t i;
    const char *s = p ? "p" : "NULL";
    const unsigned char *pp = (const unsigned char *) &p;

    printf( "p is %sNULL\n", p ? "non-" : "\0" );

    printf( "sizeof(%s): %u bytes\n", s, sizeof(p) );
    printf( "addrof(%s): (hex) ", s );
    fflush( stdout );
    for (i=0; i != sizeof(p); i++)
        printf( "%02x ", (unsigned) pp[i] );
    puts( "\n" );
}

και να της περνάς το p ως NULL (EDIT2: ή ως (void *)NULLαν και στο συγκεκριμένο δεν χρειάζεται).

 

Μπορείς επίσης να το κάνεις και απευθείας πάνω σε NULL με macro, χωρίς δηλαδή να μεσολαβήσει ενδιάμεση μεταβλητή...

 

#define PRINT_NULL_ADDRESS()					\
do {								\
	for (size_t i=0; i != sizeof(NULL); i++)		\
		printf( "%02x ", (unsigned) NULL );	\
	puts( "\n" );						\
} while(0)

@forum:

 

Στο δια ταύτα, από τη στιγμή που το NULL είναι implementation defined, προσωπικά αδυνατώ να κατανοήσω τον λόγο για τον οποίον πρέπει να στηριχτούμε στο assumption πως υλοποιείται με 0-bits.

 

Ακόμα κι όταν δεν γράφουμε portable κώδικα, είναι και πάλι εξαιρετικά σπάνιο να μας ενδιαφέρει το εσωτερικό implementation του NULL. Εφόσον μάλιστα το πρότυπο μας εγγυάται πως σε integer context η σύγκριση ενός pointer με το 0 συμπεριφέρεται όμοια με τη σύγκριση του με το NULL, ανεξαρτήτως εσωτερικής υλοποίησης του NULL, τότε γίνεται ακόμα πιο σπάνιο να μας ενδιαφέρει το πως υλοποιείται εσωτερικά το NULL.

 

Εκεί που πειράζει είναι στις αναθέσεις των δεικτών σε EDIT2: non-literal 0, θεωρώντας πως το 0 ισούται με NULL ή έστω αυτό έχω εντυπώσει εγώ. Δεν θυμάμαι καν πότε ήταν η τελευταία φορά που έχω μπει στην διαδικασία να παιδευτώ άδικα με αυτή την λεπτομέρεια, διότι δεν αναθέτω ποτέ έναν δείκτη απευθείας σε 0 όταν δουλεύω με C... πάντα τον αναθέτω σε NULL και αδιαφορώ πλήρως (ως οφείλω να κάνω σε high-level) για το τι ακριβώς κάνει εσωτερικά ο οποιοσδήποτε compiler.

 

EDIT1:

 

Αλλαγή στον κώδικα της συνάρτησης και του macro από το περιττό (long unsigned) σε (unsigned) στο casting της εκτύπωσης.

 

EDIT2:

 

Βλέπε: http://www.insomnia.gr/topic/484331-malloc-free/page-3?do=findComment&comment=52328603.

Επεξ/σία από migf1
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Update:

 

Έχω χρόνο σήμερα, οπότε το 'ψαξα λίγο παραπάνω, μιας και ήθελα να ξεκαθαρίσω ξανά και στο δικό μου μυαλό την ισοδυναμία του NULL με το literal 0 στην C.

 

Διάβασα λοιπόν αυτό εδώ:  http://www.lysator.liu.se/c/c-faq/c-1.html (από το c-faq) που είναι μια πιο συνοπτική παρουσίαση των όσων περιέχει το link που είχα δώσει σε προηγούμενο ποστ και νομίζω πως αυτό που πρέπει να κρατήσει κανείς είναι η απάντηση στην ερώτηση: 1.12.

 

Οπότε το...

whateverType *p = 0;

περνάει πάντα από οποιονδήποτε C compiler και είναι ταυτόσημο του...

whateverType *p = NULL;

ανεξάρτητα από την εσωτερική υλοποίηση του NULL.

 

Παραμένει πως οποιαδήποτε άλλη σταθερά πλην της 0 παράγει warning σε οποιονδήποτε ANSI+ C compiler.

 

Με άλλα λόγια, σε source code context το NULL δεν είναι τίποτε άλλο από syntactic sugar για το 0 ( ή το (void *)0 ), το οποίο όμως όταν ανατίθεται σε δείκτη (και μόνο τότε) συμπεριφέρεται σαν να ήταν κάποιο ειδικό keyword της γλώσσας που μετατρέπει αυτόματα τον δείκτη στην εσωτερική υλοποίηση του null-pointer του εκάστοτε compiler (0-bits ή μη). Και μάλιστα λαμβάνοντας αυτόματα υπόψη και το μέγεθος του δείκτη p, το οποίο μπορεί να είναι διαφορετικό για κάθε τύπο.

 

Επίσης, ανεξαρτήτως εσωτερικής υλοποίησης του NULL, όταν θέλουμε να περάσουμε ως όρισμα σε μια συνάρτηση έναν null-pointer μπορούμε να χρησιμοποιήσουμε είτε 0 είτε NULL, αρκεί πρώτα να το κάνουμε cast στον τύπο του δείκτη που αναμένει η συνάρτηση.

 

Το πρόβλημα που υπάρχει με την memset() είναι πως δεν χρησιμοποιεί απευθείας ανάθεση του συμβόλου 0 σε κάποιον δείκτη (περίπου δηλαδή αυτό που έλεγε ο ημίθεος για ενδιάμεση μεταβλητή, αλλά όχι ακριβώς) αλλά είναι απολύτως φυσιολογικό να δουλεύει σε compilers που χρησιμοποιούν 0-bits implementation (σε αντίθεση δηλαδή με αυτό που έλεγε ο ημίθεος πως δεν θα έπρεπε).

 

Παραμένει φυσικά ως πρόβλημα σε compilers που δεν χρησιμοποιούν 0-bits implementation. Στο c-faq το εξηγεί κι αυτό, στην απάντηση της ερώτησης 3.13, αλλά με σημείο αναφοράς την calloc() που είναι το ίδιο πράγμα.

 

Εν κατακλείδι, το literal 0 και το NULL είναι ταυτόσημα όταν ανατίθενται σε δείκτες, με το NULL να θεωρείτε ως καλύτερη πρακτική αναφορικά με το readability (και κατά περίπτωση με καλύτερο checking ανάλογα το implementation, όταν δηλαδή ορίζεται ως (void *)0 ).

 

Οπότε, στο παρακάτω..

whatever *p1 = 0, *p2 = p1;
whatever *p3 = NULL, *p4 = p3;

όλοι οι δείκτες γίνονται null-pointers ανεξαρτήτως implementation,

 

ενώ στο παρακάτω...

int n = 0;
void *p = (void *)n;

εξακολουθώ να μην έχω ξεκάθαρο το τι ακριβώς γίνεται σε non-0-bits implementation :P

Σε 0-bits το θεωρεί NULL πάντως, as expected (just checked it).

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

Δημοσ. (επεξεργασμένο)

migf1, on 29 Apr 2013 - 06:56 AM, said:

Καταρχήν, το ...

void* p2 = 0xabad1dea;
δεν περνάει χωρίς warning από κανέναν ANSI+ C compiler έχει πέσει στα χέρια μου. Για να περάσει πρέπει να κάνεις cast το 0xabad1dea σε (void *) ή σε όποιο άλλο type ορίζεις τον p2.

 

 

Τα casts σε void* και η συμπεριφορά του %p στην printf όντως έτσι είναι (π.χ. το 0xabad1dea είναι integer literal οπότε προφανώς χρειάζεται cast για να γίνει pointer) αλλά ο σκοπός μου ήταν να φωτογραφίσω αυτά που περιγράφω σε κώδικα, όχι να κάνω compile. Νομίζω πως όλα αυτά δεν αλλάζουν τίποτα όσον αφορά την ουσία του post μου.

 

migf1, on 29 Apr 2013 - 06:56 AM, said:

Από την στιγμή λοιπόν που το κάνεις cast, ουσιαστικά το παίρνεις πάνω σου και λες του compiler να κάνει treat το 0xabad1dea ως διεύθυνση ενός object of type void (ή όποιον άλλον τύπο έχεις ορίσει για το p2).

 

Συγκεκριμένα για το cast στο void* p2 = 0xabad1dea δεν ισχύει αυτό που λες ότι "εφόσον το παίρνεις πάνω σου θεωρείται πως ο p2 τώρα δείχνει σε object". Δύο διαφορετικοί λόγοι γιατί, καθένας εκ των οποίων απο μόνος του είναι αρκετός:

 

1. Ορισμός του object: "region of data storage in the execution environment, the contents of which can represent values". Προφανώς το p2 δεν δείχνει σε region of data storage μιας και δεν ξέρεις τι έχει εκεί. Δεν ξέρεις ούτε αν έχεις οποιαδήποτε access σε κείνη τη memory address (αν και αυτό βέβαια είναι εκτός του machine model της C), ούτε καν αν υπάρχει αυτή η memory address (δεν το εγγυάται κανείς).

 

2. Αν ήταν έτσι όπως τα λες τότε είτε αυτό δε θα έπρεπε να τυπώνει 1, είτε ο gcc 4.7.2 δεν είναι conformant (τέλος πάντων με τα flags του compilation), είτε το quote του ημίθεου από το πρότυπο είναι λάθος. Επειδή τίποτα από τα τρια δε νομίζω πως συμβαίνει, δια του αποκλεισμού τα πράγματα δεν είναι όπως τα λες.

 

migf1, on 29 Apr 2013 - 06:56 AM, said:

Έπειτα, το ...

void* p1 = 0;
περνάει χωρίς warning όταν το NULL υλοποιείται με 0-bits, διότι δεν έχει κανένα νόημα να βγάλει warning, αλλά δεν είμαι καθόλου σίγουρος πως περνάει σε compilers που δεν υλοποιούν το NULL με 0-bits (έχουν περάσει πολλά χρόνια από την τελευταία φορά που χρειάστηκε να δουλέψω σε τέτοιες πλατφόρμες). Λογικά δεν περνάει (EDIT2: βλέπε http://www.insomnia.gr/topic/484331-malloc-free/page-3?do=findComment&comment=52328603), αλλά η ουσία είναι πως αν αντί για 0 χρησιμοποιείς NULL τότε περνάει πάντα & παντού και δουλεύει πάντα & παντού.

 

 

Όπως συνειδητοποιείς και μόνος σου παρακάτω αυτό είναι 100% legal όπως έχει περιγράψει και ο ημίθεος αρκετές φορές στα προηγούμενα post του και όπως λέει τόσο το πρότυπο όσο και το faq στο οποίο δώσαμε και οι 2 link νωρίτερα.

 

"An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant".

 

To cast είναι προαιρετικό εδώ. Υπάρχουν implementations όπου το null ορίζεται ως 0L (το λέει και στο faq).

 

migf1, on 29 Apr 2013 - 06:56 AM, said:

Στο δια ταύτα, από τη στιγμή που το NULL είναι implementation defined, προσωπικά αδυνατώ να κατανοήσω τον λόγο για τον οποίον πρέπει να στηριχτούμε στο assumption πως υλοποιείται με 0-bits.

 

Ακόμα κι όταν δεν γράφουμε portable κώδικα, είναι και πάλι εξαιρετικά σπάνιο να μας ενδιαφέρει το εσωτερικό implementation του NULL. Εφόσον μάλιστα το πρότυπο μας εγγυάται πως σε integer context η σύγκριση ενός pointer με το 0 συμπεριφέρεται όμοια με τη σύγκριση του με το NULL, ανεξαρτήτως εσωτερικής υλοποίησης του NULL, τότε γίνεται ακόμα πιο σπάνιο να μας ενδιαφέρει το πως υλοποιείται εσωτερικά το NULL.

 

Εκεί που πειράζει είναι στις αναθέσεις των δεικτών σε EDIT2: non-literal 0, θεωρώντας πως το 0 ισούται με NULL ή έστω αυτό έχω εντυπώσει εγώ. Δεν θυμάμαι καν πότε ήταν η τελευταία φορά που έχω μπει στην διαδικασία να παιδευτώ άδικα με αυτή την λεπτομέρεια, διότι δεν αναθέτω ποτέ έναν δείκτη απευθείας σε 0 όταν δουλεύω με C... πάντα τον αναθέτω σε NULL και αδιαφορώ πλήρως (ως οφείλω να κάνω σε high-level) για το τι ακριβώς κάνει εσωτερικά ο οποιοσδήποτε compiler.

 

Κανείς δεν έκανε τέτοιο assumption και όλα αυτά δεν έχουν σημασία εδώ γιατί μιλάμε πάντα για literal 0 (εκτός από το παράδειγμα στο ideone όπου επίτηδες χρησιμοποιώ non-literal 0 για να κάνω το point μου). Τώρα που τα φρέσκαρες κατάλαβες.

Με την ευκαιρία να επιστήσω την προσοχή στο ότι το all zero bits και το integer 0 είναι θεωρητικά δύο διαφορετικά πράγματα, οπότε έχει σημασία αν θα εκφραστείς χρησιμοποιώντας το ένα ή το άλλο. Για παράδειγμα σε μια υποτιθέμενη υλοποίηση η bitwise αναπαράσταση των αριθμών μπορεί να είναι "το XOR του two's complement με 0xE665A1AD", οπότε το integer 0 αναπαρίσταται με 0xE665A1AD και το all zero bits αντιστοιχεί στο unsigned 429547090 δεκαδικό.

 

(Και όλα αυτά είναι ανεξάρτητα από την αριθμητική τιμή του null pointer η οποία μπορεί να είναι οποιαδήποτε).

 

Το συμπέρασμα που βγάζω διαβάζοντας τα 2 τελευταία post σου είναι πως αυτή η λεπτομέρεια σου διαφεύγει (χρησιμοποιείς πολλές φορές το "0-bits" εκεί που το σωστό θα ήταν integer 0).

 

Update: όπως προκύπτει από παρακάτω posts, all zero bits και int 0 τελικά είναι το ίδιο πράγμα. Η αριθμητική τιμή του null pointer όμως δεν είναι απαραίτητα μηδέν.

 

migf1, on 29 Apr 2013 - 10:24 AM, said:

Update:

 

Έχω χρόνο σήμερα, οπότε το 'ψαξα λίγο παραπάνω, μιας και ήθελα να ξεκαθαρίσω ξανά και στο δικό μου μυαλό την ισοδυναμία του NULL με το literal 0 στην C.

 

Χαίρομαι που πλέον είμαστε όλοι on the same page. :P

 

migf1, on 29 Apr 2013 - 10:24 AM, said:

ενώ στο παρακάτω...

int n = 0;void *p = (void *)n;
εξακολουθώ να μην έχω ξεκάθαρο το τι ακριβώς γίνεται σε non-0-bits implementation :P

Σε 0-bits το θεωρεί NULL πάντως, as expected (just checked it).

 

 

Ο παρατηρητικός αναγνώστης συνειδητοποιεί πως αυτό το αποτέλεσμα (που είναι το ίδιο με το παράδειγμα που έβαλα στο ideone, δεν το είδα εδώ εγκαίρως να μη μπω στον κόπο) σημαίνει πως το p δεν είναι pointer σε object όπως λες παραπάνω. Ο μόνος λόγος που το αναφέρω και πάλι είναι γιατί ίσως δεν κατάλαβες πως αντιφάσκεις.

 

Τώρα όσον αφορά το τι γίνεται εδώ, βασικά γίνεται ο,τι κάτσει. Είναι null μόνο σε implementations όπου η αναπαράσταση του integer 0 είναι ίδια με την αναπαράσταση του null pointer (δηλαδή εξαρτάται από δύο ανεξάρτητα μεταξύ τους πράγματα: πρώτον από την bitwise τιμή του null pointer και δεύτερον από την αναπαράσταση των ακέραιων στη συγκεκριμένη πλατφόρμα).

 

Update: (bonus συμπέρασμα)

 

6.5.9/6:

 

"Two pointers compare equal if and only if both are null pointers [...]"

 

Επομένως βάσει του παραπάνω παραδείγματος και θεωρώντας ότι ο compiler ήταν conformant, μπορείς να κάνεις ένα DIY null pointer ο οποίος είναι το ίδιο "επίσημος" όσο το NULL αν τυχαίνει να ξέρεις ποιά είναι η bitwise αναπαράσταση του NULL στη συγκεκριμένη πλατφόρμα.

 

Δηλαδή στα κατα τα γνωστά x86, το παράδειγμα ναι μεν "δουλεύει κατα τύχη" αλλά αυτό το κατα τύχη είναι implementation-defined και όχι undefined behavior.

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

Χαίρομαι που πλέον είμαστε όλοι on the same page. :P

Ξεκινάω από αυτό, διότι δεν νομίζω πως είμαστε on the same page.

 

Και δεν το νομίζω διότι η συζήτηση ξεκίνησε με σημείο αναφοράς το NULL για δείκτη οποιουδήποτε τύπου, ενώ εσύ τώρα προσπαθείς να την επικεντρώσεις γύρω από τον τύπο void, ο οποίος αποτελεί ειδική περίπτωση... δηλαδή την εξαίρεση και όχι τον κανόνα. Ακόμα κι έτσι όμως, στη C το (void *) για data αναφέρεται conceptually σε untyped object, και μάλιστα γίνεται implicitly casted σε δείκτη οποιουδήποτε άλλου τύπου (κάτι που δεν γίνεται σε C++ και ίσως για αυτό δυσκολεύεσαι να καταλάβεις το προηγούμενο post μου). Που σημαίνει δηλαδή πως το void * μπορεί κάλλιστα να αναφέρεται και να γίνει implicitly casted και σε primary τύπο και σε user-defined τύπο (struct), και σε (σχεδόν) οτιδήποτε άλλο, κλπ... με μια λέξη: object.

 

Επειδή όμως η κουβέντα δεν αφορά ειδικά τους δείκτες τύπου void στη C, αλλά το NULL για δείκτες οποιουδήποτε τύπου, η παράθεση που έκανα στο ποστ σου με σχόλιο πως δεν περνάει χωρίς warning από κανέναν ANSI+ compiler δεν έχει απολύτως καμία σχέση με τον τύπο void που είχες στο παράδειγμά σου και το οποίο σχολίασα.

 

Ακριβώς το ίδιο warning θα πάρεις σε οποιονδήποτε ANSI+ C ανεξαρτήτως του τύπου του δείκτη που αφήνεις uncasted.

 

Π.χ...

 

int *p2 = 0xabad1dea;
σου δίνει ακριβώς το ίδιο warning που σου δίνει οποιοδήποτε τύπο κι αν βάλεις αντί για int.

 

Τα casts σε void* και η συμπεριφορά του %p στην printf όντως έτσι είναι (π.χ. το 0xabad1dea είναι integer literal οπότε προφανώς χρειάζεται cast για να γίνει pointer) αλλά ο σκοπός μου ήταν να φωτογραφίσω αυτά που περιγράφω σε κώδικα, όχι να κάνω compile. Νομίζω πως όλα αυτά δεν αλλάζουν τίποτα όσον αφορά την ουσία του post μου.

Δεν είναι μόνο η συμπεριφορά του cast σε (void *) έτσι, αλλά του cast σε οποιοδήποτε (τύπος *)

 

Συγκεκριμένα για το cast στο void* p2 = 0xabad1dea δεν ισχύει αυτό που λες ότι "εφόσον το παίρνεις πάνω σου θεωρείται πως ο p2 τώρα δείχνει σε object". Δύο διαφορετικοί λόγοι γιατί, καθένας εκ των οποίων απο μόνος του είναι αρκετός:

 

1. Ορισμός του object: "region of data storage in the execution environment, the contents of which can represent

values". Προφανώς το p2 δεν δείχνει σε region of data storage μιας και δεν ξέρεις τι έχει εκεί. Δεν ξέρεις ούτε αν έχεις οποιαδήποτε access σε κείνη τη memory address (αν και αυτό βέβαια είναι εκτός του machine model της C), ούτε καν αν υπάρχει αυτή η memory address (δεν το εγγυάται κανείς).

 

2. Αν ήταν έτσι όπως τα λες τότε είτε αυτό δε θα έπρεπε να τυπώνει 1, είτε ο gcc 4.7.2 δεν είναι conformant (τέλος πάντων με τα flags του compilation), είτε το quote του ημίθεου από το πρότυπο είναι λάθος. Επειδή τίποτα από τα τρια δε νομίζω πως συμβαίνει, δια του αποκλεισμού τα πράγματα δεν είναι όπως τα λες.

Δες τι σου γράφω παραπάνω. Επίσης που το βρίσκεις το παράξενο που σου δίνει αποτέλεσμα 1 ο κώδικας που έγραψες σε 0-bits implementation? Το ερώτημα είναι τι θα κάνει σε non-0-bits implementation.

 

Όπως συνειδητοποιείς και μόνος σου παρακάτω αυτό είναι 100% legal όπως έχει περιγράψει και ο ημίθεος αρκετές φορές στα προηγούμενα post του και όπως λέει τόσο το πρότυπο όσο και το faq στο οποίο δώσαμε και οι 2 link νωρίτερα.

 

"An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant".

 

To cast είναι προαιρετικό εδώ. Υπάρχουν implementations όπου το null ορίζεται ως 0L (το λέει και στο faq).

Το point για μένα είναι πως αν θυμάσαι πως το NULL δεν υλοποιείται πάντα με 0-bits και χρησιμοποιείς πάντα NULL αντί για 0 δεν χρειάζεται να θυμάσαι άλλες λεπτομέρειες.

 

Κανείς δεν έκανε τέτοιο assumption και όλα αυτά δεν έχουν σημασία εδώ γιατί μιλάμε πάντα για literal 0 (εκτός από το παράδειγμα στο ideone όπου επίτηδες χρησιμοποιώ non-literal 0 για να κάνω το point μου). Τώρα που τα φρέσκαρες κατάλαβες.

Το οποίο point είναι ποιο;

 

Με την ευκαιρία να επιστήσω την προσοχή στο ότι το all zero bits και το integer 0 είναι θεωρητικά δύο διαφορετικά πράγματα, οπότε έχει σημασία αν θα εκφραστείς χρησιμοποιώντας το ένα ή το άλλο. Για παράδειγμα σε μια υποτιθέμενη υλοποίηση η bitwise αναπαράσταση των αριθμών μπορεί να είναι "το XOR του two's complement με 0xE665A1AD", οπότε το integer 0 αναπαρίσταται με 0xE665A1AD και το all zero bits αντιστοιχεί στο unsigned 429547090 δεκαδικό.

 

(Και όλα αυτά είναι ανεξάρτητα από την αριθμητική τιμή του null pointer η οποία μπορεί να είναι οποιαδήποτε).

 

Το συμπέρασμα που βγάζω διαβάζοντας τα 2 τελευταία post σου είναι πως αυτή η λεπτομέρεια σου διαφεύγει (χρησιμοποιείς πολλές φορές το "0-bits" εκεί που το σωστό θα ήταν integer 0).

Εγώ για να είμαι ειλικρινής το συμπέρασμα που βγάζω είναι πως σου διαφεύγει τελείως πως το C standard ορίζει ως 0 value για τα integer types να είναι μηδενικά όλα τα bits τους (sign bit included).

 

Ο παρατηρητικός αναγνώστης συνειδητοποιεί πως αυτό το αποτέλεσμα (που είναι το ίδιο με το παράδειγμα που έβαλα στο ideone, δεν το είδα εδώ εγκαίρως να μη μπω στον κόπο) σημαίνει πως το p δεν είναι pointer σε object όπως λες παραπάνω. Ο μόνος λόγος που το αναφέρω και πάλι είναι γιατί ίσως δεν κατάλαβες πως αντιφάσκεις.

Ακόμα δεν έχω καταλάβει σε τι αντιφάσκω.

 

Τώρα όσον αφορά το τι γίνεται εδώ, βασικά γίνεται ο,τι κάτσει. Είναι null μόνο σε implementations όπου η αναπαράσταση του integer 0 είναι ίδια με την αναπαράσταση του null pointer (δηλαδή εξαρτάται από δύο ανεξάρτητα μεταξύ τους πράγματα: πρώτον από την bitwise τιμή του null pointer και δεύτερον από την αναπαράσταση των ακέραιων στη συγκεκριμένη πλατφόρμα).

Προτιμώ να περιμένω μια σίγουρη απάντηση από κάποιον που έχει πρόσβαση σε non-0-bits implementation και θα δοκιμάσει επί τούτου με κώδικα (αν βρεθεί κάποιος δηλαδή... αν έχει κάποιος 68K μηχανάκια νομίζω είχαν non-0-bits implementation) και να μας γράψει τα αποτελέσματα.

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

Δημοσ. (επεξεργασμένο)

Και δεν το νομίζω διότι η συζήτηση ξεκίνησε με σημείο αναφοράς το NULL για δείκτη οποιουδήποτε τύπου, ενώ εσύ τώρα προσπαθείς να την επικεντρώσεις γύρω από τον τύπο void, ο οποίος αποτελεί ειδική περίπτωση...

 

Ορίστε; Τι σημασία έχει αν είναι void* ή οτιδήποτε άλλο; Βάλε char* αν θέλεις όπου παραπάνω λέει void*, δεν αλλάζει απολύτως τίποτα (γενικά μιλώντας, όχι ότι πήγα να το ελέγξω).

 

Ακόμα κι έτσι όμως, στη C το (void *) για data αναφέρεται conceptually σε untyped object, και μάλιστα γίνεται implicitly casted σε δείκτη οποιουδήποτε άλλου τύπου (κάτι που δεν γίνεται σε C++ και ίσως για αυτό δυσκολεύεσαι να καταλάβεις το προηγούμενο post μου). Που σημαίνει δηλαδή πως το void * μπορεί κάλλιστα να αναφέρεται και να γίνει implicitly casted και σε primary τύπο και σε user-defined τύπο (struct), και σε (σχεδόν) οτιδήποτε άλλο, κλπ... με μια λέξη: object.

 

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

 

Επειδή όμως η κουβέντα δεν αφορά ειδικά τους δείκτες τύπου void στη C [..]

Δεν είναι μόνο η συμπεριφορά του cast σε (void *) έτσι, αλλά του cast σε οποιοδήποτε (τύπος *)

 

Δε βλέπω τι σχέση έχουν όλα αυτά. Είπαμε για το warning, αλλά στην πράξη δεν αλλάζει τίποτα στο ενδεικτικό παράδειγμα που έδωσα. Ξαναλέω: η ουσία παραμένει.

 

Δες τι σου γράφω παραπάνω. Επίσης που το βρίσκεις το παράξενο που σου δίνει αποτέλεσμα 1 ο κώδικας που έγραψες σε 0-bits implementation? Το ερώτημα είναι τι θα κάνει σε non-0-bits implementation.

 

Ουφφφ....

 

Κάνε σε παρακαλώ τον κόπο να ξαναδιαβάσεις το post του ημίθεου με το το quote από το 6.3.2.3. Στο παρέδειγμα συγκρίνω (και εσύ το ίδιο) έναν "αυθεντικό" null pointer με έναν τυχαίο pointer που έφτιαξα μόνος μου (και του έδωσα την τιμή 0 αλλά αυτό δε μας ενδιαφέρει στα πλαίσια της συζήτησης). Οι δύο pointers έκαναν compare ίσοι. Άρα, με τις παραδοχές που ανέφερα παραπάνω ο pointer που έφτιαξα εγώ δεν είναι pointer to object γιατί τότε υποχρεωτικά από την 6.3.2.3 το αποτέλεσμα της σύγκρισης θα έπρεπε να είναι false.

 

Και ξαναματαλέω: η αντίληψή σου για το τι είναι object δε συμφωνεί με την έννοια που δίνει το πρότυπο σ' αυτή τη λέξη.

 

Το οποίο point είναι ποιο;

 

Το αυτό από πάνω.

 

Εγώ για να είμαι ειλικρινής το συμπέρασμα που βγάζω είναι πως σου διαφεύγει τελείως πως το C standard ορίζει ως integer 0 value να είναι μηδενικά όλα τα bits.

 

Όντως μου διέφευγε (αν και δεν ήταν for lack of trying που λένε).

 

Αυτό δεν ισχύει σε καμία περίπτωση και σου προτείνω να διαβάσεις προσεκτικά όλη την 6.2.6 "Representations of types" για να τα φρεσκάρεις κι αυτά.

 

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

 

Αυτή η συμπεριφορά δε βοηθάει καθόλου ούτε τη σχέση μας ούτε τους υπόλοιπους που διαβάζουν τα αντίστοιχα επικά thread.

 

Τα λάθη είναι ανθρώπινα, αλλά εδώ η 6.2.6 is screaming ότι τα πράγματα δεν είναι όπως τα λες σε καμία περίπτωση. Εντελώς ενδεικτικό quote:

 

For signed integer types, the bits of the object representation shall be divided into three

groups: value bits, padding bits, and the sign bit.

 

[...]

 

The values of any padding bits are unspecified.

Προφανώς μπορώ να βάλω όσα padding bits θέλω στην αναπαράσταση του 0 και να πω ότι θα έχουν όποια τιμή μου αρέσει.

 

Ακόμα δεν έχω καταλάβει σε τι αντιφάσκω.

 

Αν είχες δίκιο για τον ορισμό του object τότε οι δύο pointers που συγκρίναμε δε θα έπρεπε να είναι ίσοι σε κανένα implementation βάσει της 6.3.2.3. Το γεγονός ότι από τη μία σου φαίνεται λογικό να συγκρίνουν ίσοι και από την άλλη δίνεις τον Χ ορισμό για το τι είναι object είναι αντίφαση.

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

defacer έχεις βαλθεί πάλι να με τρελάνεις μου φαίνεται :P

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

...
6.2.6.2 Integer types

5 The values of any padding bits are unspecified.45) A valid (non-trap) object representation
of a signed integer type where the sign bit is zero is a valid object representation of the
corresponding unsigned type, and shall represent the same value. For any integer type,
the object representation where all the bits are zero shall be a representation of the value
zero in that type
.
...

Δηλαδή το έλεος το ίδιο :P

 

Σε ότι αφορά το περί object, επέμεινα στο προηγούμενο ποστ μου διότι εισέπραξα πως είχες ένσταση περί object επειδή το παράδειγμα ήταν με void * όταν έγραψα πως με το cast λες του compiler να το κάνεις treat ως δείκτη σε object (όχι δηλαδή ότι θα δημιουργήσει obkect) ... θεώρησα δηλαδή πως σε χάλασε το ότι το void δεν είναι καθαρός τύπος.

 

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

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

defacer έχεις βαλθεί πάλι να με τρελάνεις μου φαίνεται :P

 

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

 

Δηλαδή το έλεος το ίδιο :P

ΟΚ, οπότε αντί να πεις "το έλεος το ίδιο" γιατί δεν κάνεις αυτό που έκανα και γω μόλις τώρα: πήγα να ξαναδιαβάσω το συγκεκριμένο section μπας και ήταν τύφλωση (δε βρήκα τίποτα σαν αυτό που λες), έκανα search σε όλο το έγγραφο (δε βρήκα τίποτα σαν αυτό που λες) και μετά μου ξημέρωσε ότι μπορεί να κοιτάμε διαφορετικά πρότυπα.

 

Και όντως, σε C90 τα πράγματα είναι ακριβώς όπως τα λες αλλά σε C99 η συγκεκριμένη πρόταση που έχεις με bold δεν υπάρχει. Οπότε σημειώσατε Χ. :-)

 

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

 

Για το object η ένστασή μου δεν ήταν αυτή. Απλά για να το πω σύντομα, object είναι "ένα πράγμα του οποίου μπορείς να πάρεις την address". To ότι μπορείς να γράψεις π.χ. char* pc = (char*) 0xabad1dea δεν κάνει αυτομάτως τον pc "pointer to object" (εφόσον εκείνη τη στιγμή στο runtime δεν υπάρχει κανένα πράγμα του οποίου αν πάρεις την address αυτή θα είναι 0xabad1dea).

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

Το point για μένα είναι πως αν θυμάσαι πως το NULL δεν υλοποιείται πάντα με 0-bits και χρησιμοποιείς πάντα NULL αντί για 0 δεν χρειάζεται να θυμάσαι άλλες λεπτομέρειες.

Παραβλέποντας την λάθος ερμηνεία μου που εξήγησε ο defacer, αυτό που τόνισα πολλές φορές (και μου φάνηκε ότι το κατάλαβες διαβάζοντας το μήνυμά που λες "είχα χρόνο και το έψαξα") είναι ότι δεν έχει καμμία σχέση πώς υλοποιείται ο null δείκτης και ούτε χρειάζεται να το ξέρεις και να το θυμάσαι. Η σταθερά 0 πάντα θα δημιουργεί null δείκτη. Σε μια πλατφόρμα που ο κενός δείκτης είναι στην διεύθυνση 0xbadbad, η έκφραση "int *p = 0" όταν γίνει assembly θα θέτει την τιμή 0xbadbad και όχι τη τιμή 0x00.

 

Δεν χρειάζεται δηλαδή να ξέρεις καμμία χαμηλού επιπέδου λεπτομέρεια με βάση την οποία θα επιλέξεις ποια έκφραση να χρησιμοποιείς. NULL και 0 έχουν ακριβώς το ίδιο αποτέλεσμα. Το μόνο πλεονέκτημα που έχει το NULL είναι σε συναρτήσεις μεταβλητού αριθμού παραμέτρων αλλά μόνο σε C compilers. Σε Visual Studio και άλλους C++ compiler ορίζεται ως 0 για αυτό το λόγο υπάρχουν κάποιοι λένε ότι δεν μπορείς να βασιστείς στη portable συμπεριφορά του NULL. Όλα αυτά βέβαια είναι ψιλά γράμματα και το 90% των χρηστών μπορούν να χρησιμοποιούν όποιο τους αρέσει χωρίς να φοβούνται.

 

ΥΓ: Δεν σας προλαβαίνω :P Μέχρι να γράψω το παρόν βρήκα άλλα δύο μηνύματα :P

 

Και όντως, σε C90 τα πράγματα είναι ακριβώς όπως τα λες αλλά σε C99 η συγκεκριμένη πρόταση που έχεις με bold δεν υπάρχει. Οπότε σημειώσατε Χ. :-)

Στην έκδοση του C99 με ενσωματωμένη την TC3 αναθεώρηση το αναφέρει στη σελίδα 51 του pdf (39 πραγματική). Μήπως κοιτούσες την αρχική έκδοση του C99 προτύπου ? Σε C90 λογικά δεν θα πρέπει να γράφει κάτι γιατί έχω την εντύπωση πως δεν είχαν οριστεί επίσημα τα padding bits.

 

Γενικά έχεις δίκιο ότι τα padding bits μπορεί να έχουν οποιαδήποτε τιμή θέλει η υλοποίηση ή κάποιες τιμές να αποτελούν trap. Ειδικά όμως για την τιμή του μηδενός μπήκε στην διορθωτική αναθεώρηση TC2 αυτή η δικλείδα που διάβασε ο migf1. Εδώ βλέπουμε την Defect Report που ήταν η αφορμή για να εισηχθεί αυτή η πρόταση επειδή η memset(τάδε, 0, τάδε) ήταν πολύ κοινή πρακτική και μέχρι τότε μπορούσε όπως είπες να έχει διαφορετική συμπεριφορά σε κάθε πλατφόρμα.

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

Ότι να ναι, όταν κάνω Debug φαίνεται ότι δουλεύει μια χαρά, όταν κάνω Release το project και τρέξω το exe τότε κρατάει τις παλιές τιμές...

 

 

 

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define N 5

struct myStruct
{
	int *Array;
};

void Create(int *myArray);
void Print(int *myArray);
void Deallocate(int *item);

void main()
{
	// Space allocation
	myStruct *mys;
	mys = (myStruct *)malloc(sizeof(myStruct));
	if (mys == NULL) 
		exit(1);

	mys->Array = (int *)malloc(sizeof(int) * (N *N));
	if (mys->Array == NULL) 
		exit(1);

	// Initial Array Values & Print
	Create(mys->Array);
	Print(mys->Array);

	// Deallocate space & Print again
	Deallocate(mys->Array);
	Print(mys->Array);

	for(int i=0; i<N; i++)
		for(int j=0; j<N; j++)
			printf("Address %d Value %d\n", &(mys->Array[i*N+j]), mys->Array[i*N+j]);
}

void Create(int *myArray)
{
	for(int i=0; i<N; i++)
		for(int j=0; j<N; j++)
			myArray[i*N+j] = 0;
}

void Print(int *myArray)
{
	for(int i=0; i<N; i++)
	{
		for(int j=0; j<N; j++) {
			printf("%d ", myArray[i*N+j]);
		}
		printf("\n");
	}
	printf("\n");
}
void Deallocate(int *item)
{
	if (item != NULL)
		free(item);
}

 

 

 

debug_release.png

Δες το http://en.wikipedia.org/wiki/Magic_number_(programming)

εκει που λεει magic debug values

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

STOP PRESS!

 

Συνέχισα να ψάχνω και ανακάλυψα ότι.... (suspense)

 

Στο αρχικό ISO πρότυπο C99 η πρόταση που δίνει ο migf1 όντως λείπει. Όμως στο WG14 N1256 (τελευταία επίσημη έκδοση του προτύπου, που περιλαμβάνει και τρία technical corrigendum όπως τα λένε) η πρόταση αυτή υπάρχει.

 

Για την ακρίβεια, η πρόταση υπάρχει ήδη από το TC2 το οποίο ήταν και αυτό που έκανα quote παραπάνω γράφοντας λανθασμένα πως ήταν το C90. Για την ακρίβεια αυτό που ήταν λάθος ήταν το filename στο σκληρό (διάβαζα το C99+TC2 νομίζοντας πως διάβαζα C90)... :X

 

Άρα η ατάκα για το μηδέν δεν αφαιρέθηκε (για την ακρίβεια δε βρίσκω καν το ίδιο κείμενο στο πραγματικό C90) αλλά αντίθετα προστέθηκε κάποια στιγμή μετά την πρώτη κυκλοφορία του C99.

 

Συμπεράσματα:

  1. migf1 έχεις δίκιο για το integer μηδέν (βέβαια στα πλαίσια της παραπάνω κουβέντας μπορούμε νομίζω αντί για το μηδέν να χρησιμοποιήσουμε άλλον αριθμό εξίσου καλά). Ελπίζω να καταλαβαίνουμε όλοι ότι το λάθος μου παραπάνω δεν ήταν εσκεμμένο. Έκανα edit κάποια προηγούμενα post όπου έγραφα ανακρίβειες σχετικά. Συγγνώμη για την αναστάτωση. Peace.
  2. Πρέπει να φροντίζω περισσότερο την αποθήκη με τα πρότυπα ακόμα και στις γλώσσες που δε χρησιμοποιώ. :unsure:
  3. Μήπως να το γυρίζαμε πλέον στο C11?
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα

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