nik324 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 Κατασκευή αφηρημενου τυπου δεδομενων, Type MakeEmptyADT(): δημιουργεί έναν κενό αφηρημενο τυπο δεδομενων
migf1 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 Κατασκευή αφηρημενου τυπου δεδομενων, Type MakeEmptyADT(): δημιουργεί έναν κενό αφηρημενο τυπο δεδομενων Τι είναι αυτό; Δώσε more info, γιατί έτσι "παλεύουμε με guessworks". Στη C, χωρίς εξειδικευμένες πληροφορίες, η κοινή πρακτική υπαγορεύει πως όπου βλέπεις "make" παραπέμπει σε malloc()/calloc() και όπου βλέπεις "makeEmpty" παραπέμπει σε κάτι που γίνεται malloc()/calloc() και είτε δεν αρχικοποιείται με κάποια τιμή είτε αρχικοποιείται με την τιμή που θεωρεί ως "άδειο" το ADT interface. Επίσης χωρίς εξειδικευμένες πληροφορίες, η κοινή πρακτική υπαγορεύει πως οι συναρτήσεις makeΧΧΧ() επιστρέφουν έναν δείκτη στο αντικείμενο που κάνουν allocate (η NULL σε περίπτωση αποτυχίας). Για αυτό σου έγραψα πριν πως δεν καταλαβαίνω τι εννοείς (κι ακόμα δεν καταλαβαίνω, χρειάζονται περισσότερες πληροφορίες).
nik324 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 Πιστευω οτι με βοηθησες αρκετα... Και παλι δεν μπορω να πω τιποτα παραπανω για λογους που καταλαβαινεις....
migf1 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 Ωραία Δεν ξέρω αν θα σε βοηθήσει (ή θα σε μπερδέψει) αλλά ρίξε μια ματιά και σε αυτό το νήμα: http://www.insomnia.gr/topic/426962-%CE%B5%CE%BA%CF%84%CF%8D%CF%80%CF%89%CF%83%CE%B7-%CE%B5%CF%84%CE%B5%CF%81%CE%BF%CE%B3%CE%B5%CE%BD%CF%8E%CE%BD-%CE%BA%CE%BF%CE%BC%CE%B2%CF%89%CE%BD-%CF%83%CF%84%CE%BF%CE%AF%CE%B2%CE%B1%CF%82-%CF%83%CE%B5-ansi-c/
ChRis6 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 @nik324 Κατι τετοιο σε βολευει ; > enum{ empty,type1,type1...};; struct TYPE_T{ int type; union { struct type1_t mytype1; struct type2_t mytype2; .... } }; 1
imitheos Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 (επεξεργασμένο) Ναι. Κοιτα καταρχην ειμαι στην σελιδα 268 και προσπαθω να καταλαβω τι γινεται εδω περα.... > for( p = &a[0] ; p < &a[NUM_ROWS] ; p++) (*p)[i] = 0; To i εδω που αυξάνεται? ο p δείχνει σε διαφορετικο πίνακα - γραμμη καθε φορα 0 γραμμη πχ ταδε στοιχειων (στηλες) κ.ο.κ οκ το καταλαβα αυτο. Δεν αυξάνεται πουθενά. Η διατύπωση λέει "Here's a loop that clears column i of the array a". Αυξάνοντας το p (το οποίο είναι δείκτης σε array και όχι απλός δείκτης σε int), κάθε φορά έχεις την εκάστοτε γραμμή. Έπειτα θέτεις την τιμή 0 στο i-οστό στοιχείο της γραμμής. Όταν έχεις δισδιάστατο πίνακα, καλό είναι για να μην υπάρχουν μπερδέματα να τον μεταχειρίζεσαι ως δισδιάστατο ή έστω με δείκτη σε array όπως το κάνει εδώ ο King. Ο χειρισμός δισδιάστατου πίνακα ως μονοδιάστατου δεν είναι καλή πρακτική. Δεν έχω διαβάσει όλο το κεφάλαιο οπότε επιφυλάσσομαι μια και ο King μπορεί να το λέει απλά για να περιγράψει διδακτικά τις έννοιες. Στις υπόλοιπες περιπτώσεις, ο προηγούμενος κώδικας από αυτόν που παρέθεσες δεν είναι τυπικά σωστός (θα παίξει παντού όμως). Edit: Ας το περιγράψω πιο αναλυτικά. Πριν βιαζόμουν. Ο King αναφέρει: int a[NUMS_ROWS][NUM_COLS]; The obvious technique would be to use nested for loops: > int row, col; for (row = 0; row < NUM_ROWS; row++) for (col = 0; col < NUM_COLS; col++) a[row][col] = 0; But if we view a as a one-dimensional array of integers (which is how it's stored), we can replace the pari of loops by a single loop: > int *p; for (p = &a[0][0]; p <= &a[NUM_ROWS-1][NUM_COLS-1]; p++) *p = 0; Μετά λέει ότι ενώ φαίνεται σαν cheat λειτουργεί κανονικά και συνεχίζει να χρησιμοποιεί και αλλού δείκτες. Επειδή είπαμε ότι τα στοιχεία των πινάκων είναι πάντα συνεχόμενα στη μνήμη, το παραπάνω λειτουργεί σχεδόν παντού. Δεν είναι όμως σωστό και μπορεί άνετα ένας compiler να βγάλει ένα χέρι και να μας σφαλιαρίσει. Μόνο με char (έχω την εντύπωση ότι μόνο με unsigned char και όχι γενικά με char αλλά δεν το θυμάμαι) μπορούμε να προσπελάσουμε οποιοδήποτε αντικείμενο στη μνήμη. Εδώ ο p είναι δείκτης σε int και όχι char οπότε τυπικά δεν είναι σωστός ο κώδικας. Οπότε όχι μόνο κανείς δεν μας εμποδίζει να ορίσουμε μονοδιάστατα έναν 2διάστατο πίνακα, και να τον διαχειριστούμε ανάλογα, αλλά πολλές φορές μας εξυπηρετεί και καλύτερα. Π.χ. σε παιχνίδια με τράπουλα, συνήθως εξυπηρετεί καλύτερα η 2διάστατη τράπουλα να οριστεί μονοδιάστα... > #define NSUITS 4 #define NFACES 13 #define NCARDS (NSUITS * NFACES) Card deck[NCARDS]; Ο παραπάνω κώδικας του migf1 είναι σωστός γιατί δεν δηλώνει δισδιάστατο πίνακα αλλά πολλαπλασιάζει τις δύο "διαστάσεις" και δηλώνει ένα μονοδιάστατο πίνακα οπότε και φυσικά μπορεί να τον χειριστεί σαν μονοδιάστατο. Εστω οτι για ενα αφηριμενο τυπο δεδομενων θελω να φτιαξω μια λειτουργια makeEmptyADT() η οποια θα φτιαχνει αυτον τον κενο αφηρημενο τυπο δεδομενων για μελλοντικη χρηση..κανω το εξης..ειναι σωστο; > Type MakeEmptyADT() { Type *ptr=NULL; return ptr; } Eυχαριστώ Έτσι που το έχεις αφενός επιστρέφει NULL και αφετέρου έχεις δηλώσει ότι η συνάρτηση επιστρέφει Type και όχι δείκτη σε Type. Σου κάνει κάτι σαν το παρακάτω ? > Type *MakeEmptyADT() { Type *ptr = malloc(sizeof(ADT)); memset(ptr, 0, sizoeof(ADT)); return ptr; } Επεξ/σία 27 Οκτωβρίου 2012 από imitheos
Star_Light Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 Αμα δεν ειναι σωστος ο κώδικας του King πρέπει να του στείλουμε mail migf1 ο κωδικας φυσικα δεν ειναι δικος μου... που λες οτι ειναι λάθος τον έχω παρει απο το βιβλιο Σελ. 269
migf1 Δημοσ. 27 Οκτωβρίου 2012 Δημοσ. 27 Οκτωβρίου 2012 (επεξεργασμένο) @nik32 Αυτό που δίνει ο Chris6 είναι ο τυπικός τρόπος διαχείρισης ADT, όταν θέλεις να υποστηρίζει πιο εξειδικευμένους τύπους. Δηλαδή, ορίζεις με έναν enumerator τους πιο εξειδικευμένους τύπους που θα υποστηρίζει το ADT σου, τον οποίον τον απαιτείς σαν όρισμα στον constructor του interface σου. Για τον κάθε εξειδικευμένο τύπο, ο constructor χρειάζεται να ξέρει και το μέγεθός του, καθώς και την εσωτερική του υλοποίηση (π.χ. αν μιλάμε για κάποιο struct, θα πρέπει να γνωρίζεις τα fileds του). Ομοίως και οποιαδήποτε συνάρτηση διαχείρισής τους, που ενδεχομένως θα παράσχει στον end-programmer το ADT interface σου. Αν τώρα μιλάς για παντελώς αφηρημένο ADT, τότε το ελάχιστο που χρειάζεται να γνωρίζει ο constructor σου είναι το μέγεθος του τύπου. Καλό είναι όμως να συνοδεύεται κι από έναν εσωτερικό δείκτη που θα κρατάει την τιμή της εκάστοτε ADT μεταβλητης, και προφανώς ένα ελάχιστο interface που θα αποτελείται από constructor & destructor για τις Adt "μεταβλητές", καθώς και έναν assignment operator για τα data τους. Για παράδειγμα... > ... typedef struct Adt { void *pData; size_t size; }Adt; ... // Constructor ----------------------------------------- Adt *adt_make( size_t size ) { Adt *ret = malloc(Adt); if ( !ret ) return NULL; ret->pData = calloc(1, size); if ( !ret->pData ) { free( ret ); return NULL; } ret->size = size; return ret; } // Destructor ----------------------------------------- bool adt_free( Adt **adt ) { if ( !adt ) return false; if ( *adt ) { if ( (*adt)->pData ) { free( (*adt)->pData ); (*adt)->pData = NULL; } free( *adt ); *adt = NULL; } return true; } // Data Assignment ----------------------------------------- bool adt_set_data( Adt *adt, const void *pData ) { if ( !adt || !adt->pData) return false; memmove( adt->pData, pData, adt->size ); return true; } Κάτι τέτοιο θα μπορούσε να χρησιμοποιηθεί ας πούμε κάπως έτσι... > int main( void ) { Adt *adt = adt_make( sizeof(int) ); if ( !adt ) { puts( "*** fatal error!" ); exit(1); } int temp = 3; adt_set_data( adt, &temp ); printf( "%d\n", *(int *)(adt->pData) ); adt_free( &adt ); system( "pause" ); exit( EXIT_SUCCESS ); } Αυτό όπως βλέπουμε από το printf() στον παραπάνω κώδικα, αφήνει την ευθύνη διαχείρισης του κάθε τύπου στον end programmer. Δηλαδή, ο end-programmer ξέρει πως πέρασε έναν int στο adt, οπότε για να το τυπώσει το κάνει cast σε int (τα void pointers δεν μπορούν να γίνουν dereferenced). Το ADT interface μας θα μπορούσε να υποστηρίζει εκτύπωση ακεραίων, παρέχοντας ενδεχομένως μια ξεχωριστή συνάρτηση η οποία θα έκρυβε αυτό το casting (και άρα θα εξάλειφε και την ανάγκη να γνωρίζει ο end-programmer την εσωτερική υλοποίηση του adt->pData ως void δεικτη)... > int adt_print_intData( const Adt *adt ) { if ( !adt || !adt->pData ) return -1; return printf( "%d", *(int *)adt->pData ); } Οπότε η main() θα άλλαζε σε... > int main( void ) { Adt *adt = adt_make( sizeof(int) ); if ( !adt ) { puts( "*** fatal error!" ); exit(1); } int temp = 3; adt_set_data( adt, &temp ); // printf( "%d\n", *(int *)(adt->pData) ); adt_print_intData( adt ); putchar( '\n' ); adt_free( &adt ); system( "pause" ); exit( EXIT_SUCCESS ); } Μπορούμε επίσης να παρέχουμε και μια generic... > bool adt_print_data( const Adt *adt, void *user, bool (*user_print)(const void *pData, void *user) ) { if (!adt || !adt->pData || !user_print ) return false; return (*user_print)(adt->pData, user); } με την οποία θα δίνουμε τη ελευθερία στον end-programmer να της περνάει ως τελευταίο όρισμα μια δική του συνάρτηση εκτύπωσης, έχοντας αυτός την ευθύνη για τον τύπο των data. Εμείς απλώς του εξασφαλίζουμε μέσα στην συνάρτηση του interface πως δεν θα φάει seg-fault αν η ADT μεταβλητή ή/και τα data της είναι ανύπαρκτα (απλώς δηλαδή παίρνουμε από τους ώμους του την ανάγκη για sanity checks κατά την εκτύπωση). Του παρέχουμε επίσης τη δυνατότητα να ελέγξει αν κάτι πήγε στραβά (του επιστρέφει false η generic συνάρτηση) καθώς επίσης και τη δυνατότητα να περάσει/επιστρέψει στην/από την συνάρτησή του οποιαδήποτε έξτρα πληροφορία επιθυμεί (μέσω του δείκτη user, στο 2ο όρισμα της generic συνάρτησης). Οπότε σε αυτή την περίπτωση (με δεδομένο πως ο end-programmer δεν ενδιαφέρεται για έξτρα ροή πληροφοριών προς και από την δικιά του συνάρτηση εκτύπωσης, άρα περνάει ως NULL το 2ο όρισμα) μπορεί να αλλάξει την main() του σε κάτι τέτοιο... > // User Defined Print Function ----------------- bool my_printpretty_intData( const void *pData, void *dummy ) { int *pdata = (void *)pData; int nchars = printf( "The value of my int data is %05d\n", *pdata ); return (nchars > 0); } // ------------------------------------------------------ int main( void ) { Adt *adt = adt_make( sizeof(int) ); if ( !adt ) { puts( "*** fatal error!" ); exit(1); } int temp = 3; adt_set_data( adt, &temp ); adt_print_data( adt, NULL, &my_printpretty_intData ); adt_free( &adt ); system( "pause" ); exit( EXIT_SUCCESS ); } Αυτή η generic συνάρτηση του interface δεν μπορεί να εξασφαλίσει στον end-programmer πως δεν θα φάει seg-fault σε περίπτωση που ως 1ο όρισμα δώσει ένα adt που περιέχει data διαφορετικού τύπου από εκείνα που θα διαχειρίζεται η custom συνάρτηση που περνάει ως 3ο όρισμα. Αν θέλουμε το interface μας να έχει όσο το δυνατόν μεγαλύτερο έλεγχο στα abstract δεδομένα που διαχειρίζονται οι συναρτήσεις του, πρέπει να το εξοπλίσουμε με όσο το δυνατόν περισσότερες πληροφορίες για τον εκάστοτε τύπο που διαχειρίζεται, που κατά βάση σημαίνει να μειώσουμε όσο το δυνατόν τα επί μέρους level of abstractions του κάθε τύπου. Για παράδειγμα, αν αποφασίζαμε πως θέλουμε ας πούμε το ADT interface μας να υποστηρίζει με managed προσέγγιση τον τύπο int, τότε επανερχόμαστε σε αυτό που προτείνει ο Chris6. Δηλαδή, προσθέτουμε για παράδειγμα έναν enumerator για την αναγνώριση του τύπου int, προσθέτουμε ένα αντίστοιχο πεδίο στον ορισμό του struct Adt και απαιτούμε τον enumerator ως όρισμα στον constructor... > enum AdtTypeId { ... ADT_TID_INT ... }; typedef struct Adt { enum AdtTypeId tid; void *pData; size_t size; }Adt; ... Adt *adt_make_predefined( enum AdtTypeId tid ) { Adt *ret = NULL; size_t size = 0; switch( tid ) { case ADT_TID_INT: size = sizeof(int); break; ... default: return NULL; } ret = malloc(Adt); if ( !ret ) return NULL; ret->pData = calloc(1, size); if ( !ret->pData ) { free( ret ); return NULL; } ret->size = size; return ret; } δεν χρειαζόμαστε καν το size πλέον, αφού για τους pre-defined, μη-generic, τύπους του interface μας το γνωρίζουμε. Στον παραπάνω κώδικα το "υπολογίζω" με switch, αλλά μπορεί κάλλιστα να υλοποιηθεί ας πούμε σε έναν πίνακα, με indexer τον enumerator AdtTypeId. Οπότε και κάθε συνάρτηση του ADT interface μας μπορεί έτσι να κάνει expose publicly ένα generic πρότυπο με 1ο όρισμα τύπου Adt *, κι εσωτερικά (privately) να συμπεριφέρεται διαφορετικά, ανάλογα με την τιμή του adt->tid. ΥΓ. Οπότε πλέον ελπίζω φίλε nik234 να έγινε λίγο πιο σαφής η απορία που εξέφρασα αρχικά, ότι "δεν καταλαβαίνω τι εννοείς αν δεν μας δώσεις περισσότερα info". Με άλλα λόγια, ο constructor σου (που ήταν και η αρχική σου ερώτηση) μπορεί να γραφτεί με 1002 διαφορετικές εκδοχές, ανάλογα τις προδιαγραφές του ζητούμενου. Αμα δεν ειναι σωστος ο κώδικας του King πρέπει να του στείλουμε mail migf1 ο κωδικας φυσικα δεν ειναι δικος μου... που λες οτι ειναι λάθος τον έχω παρει απο το βιβλιο Σελ. 269 Μα που να καταλάβω εγώ ότι μου ζητάς εξήγηση σε παράθεση δικού μου κώδικα, χρησιμοποιώντας ως δείγμα κώδικα που προέρχεται από ξένο context; Προφανώς σου είπα πως είναι εσφαλμένος θεωρώντας πως πας να κάνεις ότι έκανε ο δικός μου κώδικας που είχες παραθέσει. Που να ξέρω εγώ π.χ. ότι το p ήταν ορισμένο ως διπλός δείκτης, και που να ξέρω εγώ γενικώς πως εκείνος ο κώδικας δεν αναφερόταν στον δικό μου που μου είχες παραθέσει αμέσως πριν και πάνω στον οποίο με ρώταγες; EDIT: Διόρθωσα τα εσφαλμένα malloc() των ret στους constructors από... > ret = malloc(size); στα σωστά... > ret = malloc(Adt); Επεξ/σία 29 Οκτωβρίου 2012 από migf1
Re4cTiV3 Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Μπορούμε να ελέγχουμε αν ένας void pointer δείχνει σε int ή long κτλ;
migf1 Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Μπορούμε να ελέγχουμε αν ένας void pointer δείχνει σε int ή long κτλ; Αν εννοείς γενικώς, όχι. Διότι αυτός είναι ο λόγος ύπαρξής του void, να αναφέρεται σε αγνώστου τύπου δεδομένα. Μπορούμε όμως να ελέγξουμε αν ισούται ως διεύθυνση με έναν οποιοδήποτε υπάρχοντα εξειδικευμένο δείκτη. Για παράδειγμα... > ... int temp, *pInt = &temp; void *pVoid = (void *)pInt; ... if ( pVoid == pInt ) ... ΥΓ. Δεν το δοκίμασα, αλλά πρέπει να είναι οκ. Αν όχι διορθώστε.
Re4cTiV3 Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Εγώ το σκέφτηκα αλλιώς, να ελέγχουμε με sizeOf().
albNik Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Ίδιο sizeof έχει ο δείκτης που δείχνει σε int , long , κάποιο struct .. κ.λ.π.
migf1 Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Εγώ το σκέφτηκα αλλιώς, να ελέγχουμε με sizeOf(). Αυτό δεν σου εξασφαλίζει πως ο δείκτης δείχνει σε κάποιον συγκεκριμένο τύπο. Έστω πως μια library ορίζει ας πούμε έναν pVoid globally ως εξής... > long long int tempLLInt = 0; void *pVoid = &tempLLInt; ... Αν δεν ξέρεις την ανάθεση στον ορισμό του pVoid (αυτό ρώτησες ουσιαστικά) πως θα βρεις με sizeof() σε τι τύπο δείχνει? Μπορείς αν θες να κάνεις... > ... extern void *pVoid; ... char c = *(char *)pVoid; short sh = *(short int *)pVoid; ... long long int longLongInt = *(long long int *)pVoid; κλπ κλπ Όλα τα παραπάνω θα σου δώσουν κάποιες valid τιμές στις μεταβλητές c, sh και longLongInt (με την προϋπόθεση προφανώς πως ο pVoid είναι ήδη valid από την Library)... αλλά η μόνη που έχει νόημα στο context της βιβλιοθήκης είναι η longLongInt (επειδή σε τέτοια data έχει οριστεί από την βιβλιοθήκη να δείχνει ο pVoid). Ίδιο sizeof έχει ο δείκτης που δείχνει σε int , long , κάποιο struct .. κ.λ.π. Νομίζω εννοούσε με sizeof() της μνήμης στην οποία δείχνει ο pVoid, ή τουλάχιστον σε αυτό στηρίζεται η προηγούμενη απάντησή μου (που χρησιμοποιεί casting αντί για sizeof(), μιας και οι void δεν μπορούν να γίνουν dereferenced... οπότε π.χ. το sizeof( *pVoid ) δεν περνάει καν από τον compiler. Αν ο φίλος Reactive εννοούσε απευθείας σύγκρισης του sizeof() του δείκτη, τότε ναι η σωστή απάντηση είναι η δική σου φίλε albNik. Όλοι οι δείκτες έχουν ίδιο size. EDIT: Έσβησα έναν περιττό ορισμό στον κώδικα. EDIT2: Έσβησα επίσης κι ένα περιττό (νομίζω) static στον ορίσμό του tempLLInt;
imitheos Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Μπορείς να ελέγχεις equality με άλλον δείκτη όπως έδειξε ο migf1 αλλά φυσικά πρέπει να έχεις τον άλλο δείκτη. Αν θέλεις γενικά να δεις σε τι δείχνει δεν γίνεται. Υπάρχουν compilers που επιτρέπουν arithmetic με void δείκτες αλλά αφενός δεν είναι portable λύση και αφετέρου τον χειρίζονται σαν char δείκτη οπότε και πάλι δεν μπορείς να βρεις σε τι δείχνει. Και το sizeof δεν βολεύει γιατί απλά θα σου δώσει το μέγεθος του δείκτη το οποίο ή θα είναι ίδιο με όλους τους άλλους δείκτες όπως γίνεται στις περισσότερες πλατφόρμες και τόνισε ο albnik ή θα είναι (λογικά) μεγαλύτερο μια και η πλατφόρμα πρέπει πάντα να υποστηρίζει μετατροπή από οποιοδήποτε δείκτη σε void και αντίστροφα. Μην ξεχνάς ότι η C επιτρέπει στον προγραμματιστή να κάνει πολλά πράγματα χωρίς να τον εμποδίζει οπότε το "δείχνω σε" είναι σχετικό. Μπορείς πχ να έχεις ένα πίνακα από int και να τον προσπελαύνεις με ένα δείκτη unsigned char *. Εδώ σε τι δείχνει ο δείκτης ? Σε char ή σε int ? Edit: Με πρόλαβε ο migf1. Το ίδιο πράγμα με περιγραφικό τρόπο λέει το παράδειγμα του.
defacer Δημοσ. 29 Οκτωβρίου 2012 Δημοσ. 29 Οκτωβρίου 2012 Για όποιον ενδιαφέρεται, δεν είναι απαραίτητο ότι όλοι οι pointers έχουν το ίδιο μέγεθος: A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements. Αν και στην πράξη πιθανότατα θα το έχουν, όποιος θέλει να βασιστεί σ' αυτό καλά θα ήταν να βάλει κάποιο static assert για να μη βρεθεί προ εκπλήξεως.
Προτεινόμενες αναρτήσεις