Ilias95 έγραψε:Ποια ακριβώς είναι η διαφορά των signed και unsigned char;
Αν κατάλαβα καλά οι unsigned char είναι αυτοί που απεικονίζονται στο ASCII table που έδωσες προηγουμένως. Οι signed;
Edit: Μήπως απλά αλλάζει ο integer ASCII code; Δηλαδή ο unsigned 'A' ισοδυναμεί με 65, και ο signed 'A' με 65 - 128 δηλ. -63;
Βασικά ίσως δεν πρέπει να σε μπλέξω από τώρα με αυτά, πιθανότατα να τα καλύπτει πιο κάτω το βιβλίο.
Σε γενικές γραμμές, ο καθένας από τους βασικούς τύπους δεδομένων (char, int, float, double) καταλαμβάνει συγκεκριμένο μέγεθος μνήμης για κάθε μεταβλητή που ορίζουμε τέτοιου τύπου. Το στάνταρ εγγυάται τα ελάχιστα μεγέθη μνήμης για τον κάθε τύπο, αλλά αφήνει ως implementation dependent το ακριβές μέγεθος που τελικά καταλαμβάνει ο κάθε τύπος στην κάθε πλατφόρμα/compiler.
Για παράδειγμα, τα ελάχιστα (εγγυημένα) μεγέθη μνήμης για τους τύπους
char και
int είναι 1 και 2 bytes αντίστοιχα. Για να δεις όμως ακριβώς πόσα bytes καταλαμβάνει ο κάθε τύπος στο δικό σου σύστημα, μπορείς να χρησιμοποιήσεις τον τελεστή
sizeof...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
...
printf( "Size of char: %u\n", sizeof(char) );
printf( "Size of int: %u\n", sizeof(int) );
printf( "Size of float: %u\n", sizeof(float) );
printf( "Size of double: %u\n", sizeof(double) );
Αφήνοντας στην άκρη προς το παρόν τους τύπους
float και
double που αντιπροσωπεύουν αριθμούς κινητής υποδιαστολής (και διαχειρίζονται με διαφορετικό τρόπο εσωτερικά) οι ακέραιοι τύποι (δηλαδή οι
char και
int) μπορούν να απεικονίζουν είτε αποκλειστικά
θετικές τιμές, είτε
θετικές και αρνητικές.
Αποκλειστικά θετικές τιμές μπορούν να απεικονίζουν όταν τους χρησιμοποιείς με το πρόθεμα
unisgned που σημαίνει
χωρίς πρόσημο. Αν τους χρησιμοποιείς με το πρόθεμα
signed σημαίνει πως μπορούν να διαχειριστούν και θετικές και αρνητικές τιμές.
Ας πάρουμε τον τύπο
int κι ας υποθέσουμε πως στο σύστημά σου καταλαμβάνει 2 bytes μνήμης, την ελάχιστη χωρητικότητα που εγγυάται το στάνταρ (στην πραγματικότητα θα καταλαμβάνει μάλλον 4 bytes, ίσως και 8, διασταύρωσε το στο σύστημά σου με:
sizeof(int) ).
Αυτά λοιπόν τα 2 bytes αποτελούνται από 16 bits, μιας και το κάθε byte αποτελείται από 8 bits (αυτό είναι χαρακτηριστικό των υπολογιστών και όχι συγκεκριμένα της C ). Για να μη σε μπλέξω (από τώρα) με endiadness καθώς και τους διαφορετικούς τρόπους εσωτερική απεικόνισης των αρνητικών αριθμών, θα κάνω στη συνέχεια ορισμένες υπερ-απλουστεύσεις, προκειμένου να είναι πιο κατανοητά...
Θεωρώντας πως η σειρά των bits αλλά και των bytes πάει από αριστερά προς τα δεξιά, με τα μεγαλύτερα πρώτα, τα 2 bytes του κάθε int διατάσσονται στη μνήμη κάπως έτσι...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
2o byte 1o byte
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
Τα bits μπορούν να είναι είτε αναμμένα (1) είτε σβηστά (0) με τα σβηστά να αντιστοιχούν σε μηδενική τιμή.
Άρα λοιπόν, αν ορίσουμε έναν ακέραιο
n χωρίς πρόσημο και του αναθέσουμε την τιμή 0...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
unsigned int n = 0;
η απεικόνισή του στη μνήμη θα είναι κάπως έτσι...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
2o byte 1o byte
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Αυτή είναι η περιβόητη δυαδική απεικόνιση (binary representation) που χρησιμοποιούν οι υπολογιστές εσωτερικά για να αποθηκεύουν πληροφορίες.
Ο κάθε αριθμός αναπαριστάνεται ανάβοντας ή/και σβήνοντας bits στις κατάλληλες θέσεις και υπολογίζοντας κατόπιν το
συνολικό άθροισμα των δυνάμεων του 2 στις θέσεις που υπάρχουν αναμμένα bits. Η μέγιστη δυνατή τιμή που μπορούν να απεικονίσουν 2 bytes είναι όταν όλα τους τα bits είναι αναμμένα, και άρα ισούται με το εξής άθροισμα...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0 =
32768 + 16383 + 8192 + ... + 4 + 2 + 1 =
65535
Άρα λοιπόν ένας
unsigned int στη C μπορεί να διαχειριστεί εγγυημένα από το στάνταρ θετικές τιμές στο εύρος
0 έως 65535 (στα 4 bytes αυτό το όριο ανεβαίνει στα 4,294,967,295 και ούτω κάθε εξής).
έγραψε:
Άλλο ένα παράδειγμα για εμπέδωση του υπολογισμού, ο αριθμός 10 αποθηκευμένος σε δυαδική μορφή, σε 2 bytes (δηλαδή σε 16 bits) ισούται με: 0000000000001010, δηλαδή με: 2^3 + 0 + 2^1 + 0 = 8 + 0 + 2 + 0 = 8 + 2
Όταν όμως ο τύπος
int δηλωθεί ως
signed int (που είναι το default αν δεν καθορίσουμε τίποτα... αντίθετα το σκέτο
char είναι implementation dependent) τότε στην πιο απλή περίπτωση 1 ακριανό bit (από τα συνολικά 16 bits των 2 bytes) χρησιμοποιείται για το πρόσημο.
Αμέσως λοιπόν χάνουμε ένα bit από το συνολικό εύρος τιμών που μπορούν να απεικονίσουν τα 2 μας bytes, συν πως ότι απομένει διαχωρίζεται στη μέση, μιας και πρέπει να απεικονιστούν και αρνητικές τιμές). Άρα το τελικό εύρος τιμών αλλάζει σε:
–32,768 έως 32,767Εκτός από
signed και
unsigned που καθορίζουν το εύρος τιμών των ακέραιων τύπων
int και
char, έστω κι έμμεσα, ειδικά για τον
int υπάρχουν και τα
short και
long (και
long long, από την αναθεώρηση C99 και μετά) που εξειδικεύουν ακόμα περισσότερο το μέγεθος που καταλαμβάνει στη μνήμη ο τύπος
int. Το
long μπορεί να χρησιμοποιηθεί και στον τύπο
double.
Για παράδειγμα, μια μεταβλητή ορισμένη ως...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
long int n;
ή ως...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
unsigned long int n;
καταλαμβάνει εγγυημένα από το στάνταρ της γλώσσας τουλάχιστον 4 bytes στην μνήμη.
Μιας και το στάνταρ εγγυάται μονάχα τα ελάχιστα μεγέθη των τύπων, θα βρεις αρκετούς compilers που για παράδειγμα υλοποιούν με 4 bytes τόσο τους
int όσο και τους
long int.
Ρίξε μια ματιά και σε αυτό το link:
http://rajkishor09.hubpages.com/hub/Dat ... C-Languageκαι σε αυτό:
http://en.wikipedia.org/wiki/C_data_typesΤα εύρη τιμών των τύπων υπάρχουν στα στάνταρ header αρχεία
limits.h (για ακέραιους) και
float.h (για κινητής υποδιαστολής) συνήθως υλοποιημένα με #define. Οι τιμές τους διαφέρουν από πλατφόρμα σε πλατφόρμα ή/και από compiler σε compiler, αλλά αν θες για παράδειγμα να χρησιμοποιήσεις/αναφερθείς με portable τρόπο στην μέγιστη ή στην ελάχιστη τιμή ενός signed int μπορείς να το κάνεις με τα
INT_MAX και
INT_MIN, αντίστοιχα (και με #include <limits.h> στον κώδικά σου).
Η αναθεώρηση C99 έχει προσθέσει κι άλλα header files, όπως το inttypes.h που καθορίζει νέους τύπους με συγκεκριμένα, ακριβή μεγέθη, όπως π.χ.:
int8_t &
uint8_t (ακριβώς 8 bits),
int16_t &
uint16_t (ακριβώς 16 bits) κλπ.