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

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

Δημοσ.

Εχω γραψει ενα κωδικα που ζηταω απο τον χρηστη να μου δωσει ενα χαρακτηρα.Διαβαζοντας λοιπον τον χαρακτηρα με την εντολη c = getchar(); . Προσπαθω να τυπωσω την τιμη του με την εντολη printf("%c",c); -ως χαρακτηρα - και printf("%d",c); -ως integer - για να επαληθευσω οτι μου δινει την ιδια τιμη με αυτην που αναφερεται στο ISO8859-7(Γνωριζοντας οτι το κωδικο ονομα για το ελληνικο αλφαβητο ειναι απο 193 (Α) - 216 (Ω) και 225 (α) - 249 (ω) )

Ετσι π.χ. αν δωσω στο προγραμμα τον χαρακτηρα " Α ", περιμενω να εμφανιστει Α (απο %c) και 193 ( απο %d) . Αντιθετως μου εμφανιζεται -50.

Γνωριζει κανεις γιατι γινεται αυτο; 

Ευχαριστω.

Δημοσ. (επεξεργασμένο)
14 ώρες πριν, Maurogiannakis είπε

Εχω γραψει ενα κωδικα που ζηταω απο τον χρηστη να μου δωσει ενα χαρακτηρα.Διαβαζοντας λοιπον τον χαρακτηρα με την εντολη c = getchar(); . Προσπαθω να τυπωσω την τιμη του με την εντολη printf("%c",c); -ως χαρακτηρα - και printf("%d",c); -ως integer - για να επαληθευσω οτι μου δινει την ιδια τιμη με αυτην που αναφερεται στο ISO8859-7(Γνωριζοντας οτι το κωδικο ονομα για το ελληνικο αλφαβητο ειναι απο 193 (Α) - 216 (Ω) και 225 (α) - 249 (ω) )

Ετσι π.χ. αν δωσω στο προγραμμα τον χαρακτηρα " Α ", περιμενω να εμφανιστει Α (απο %c) και 193 ( απο %d) . Αντιθετως μου εμφανιζεται -50.

Γνωριζει κανεις γιατι γινεται αυτο; 

Ευχαριστω.

Αυτό που βλέπεις είναι συνέπεια δύο προβλημάτων που είναι τα εξής τρία:

1) Χρησιμοποιείς %d το οποίο είναι για signed αντί για %u που είναι για unsigned δεκαδικούς. Αυτό δεν είναι πρόβλημα αφ' εαυτού και δεν θα το παρατηρούσες αν δεν είχες τα επόμενα δύο προβλήματα. Αν αλλάξεις το %d σε %u πάλι δεν θα σου παίξει σωστά.

2) Σίγουρα η μεταβλητή c σου έχει τύπο char, σωστά; Μπορεί να θέλεις να λάβεις ένα "χαρακτήρα" αλλά ο τύπος της μεταβλητής σου πρέπει να είναι int και όχι char. Αυτό μπορείς να το δεις και από τον ορισμό του prototype της getchar που είναι "int getchar(void);"

3) Ο terminal driver σου μάλλον χρησιμοποιεί unicode και όχι ISO.

Ας τα δούμε αναλυτικά με ένα παράδειγμα:

% cat iso
Á
% iconv -f iso8859-7 -t utf8 iso
Α
% cat utf
Α
	
% hexdump -Cv iso     
00000000  c1 0a                                             |..|
% hexdump -Cv utf
00000000  ce 91 0a                                          |...|

Δημιούργησα δύο αρχεία τα οποία περιέχουν και τα δύο απλά τον χαρακτήρα κεφαλαίο ελληνικό Α (και την αλλαγή γραμμής 0a). Όπως βλέπεις από την έξοδο της εντολής cat, όταν τα εμφανίζω παίρνω τον χαρακτήρας Α (το πρώτο cat το έβαλα επίτηδες και τυπώνει "σκουπίδια" επειδή προσπαθώ να εμφανίσω iso σε τερματικό που θέλει unicode και απλά τυγχάνει να μοιάζει με Α με τόνο).

Το περιεχόμενο των αρχείων φαίνεται πιο ξεκάθαρα με την hexdump. Το αρχείο που είναι σε κωδικοσελίδα ISO8859-7 έχει ένα byte με τιμή C1 δηλαδή 193 όπως σωστά είπες. Το utf8 αρχείο έχει δύο bytes με τιμές CE 91 επειδή οι ελληνικοί χαρακτήρες χρειάζονται δύο bytes.

% cat char.c
#include <stdio.h>
	int main(void)
{
    char c;
    c = getchar();
    printf("c = %c - %d - %u\n", c, c, c);
    return 0;
}
% cc char.c
% ./a.out < iso
c = Á - -63 - 4294967233
% ./a.out < utf
c = Î - -50 - 4294967246
	

Ένα απλό πρόγραμμα που δέχεται ένα χαρακτήρα με την getchar και τον επιστρέφει σε μεταβλητή τύπου char. Όπως βλέπεις, παίρνω αποτέλεσμα -50 όταν του στείλω το utf8 αρχείο και όχι το ISO. Για αυτό το λόγο σου είπα ότι ο terminal driver σου θα είναι ρυθμισμένος σε UTF. Είδες τι σου είπα πριν ότι ακόμη και %u να είχες δεν θα έπαιζε σωστά;

% cc int.c
% ./a.out < iso
i = Á - 193 - 193
% ./a.out < utf
i = Î - 206 - 206
	

Ας δούμε τώρα ακριβώς το ίδιο πρόγραμμα απλά με την αλλαγή του τύπου της μεταβλητής σε int από char. Ώπα, εδώ αφενός δεν παίρνουμε αρνητικούς αριθμούς, αφετέρου παίρνω σωστά το 193 όταν του δίνω το iso αρχείο.

Το 1ο πρόβλημα σου λοιπόν είναι ότι 99% το τερματικό σου είναι ρυθμισμένο σε utf8 και όχι σε iso οπότε ο χαρακτήρας που δίνεις από το πληκτρολόγιο μεταφράζεται στα 2 bytes CE 91. Το 0xCE σε δεκαδικό είναι το 206 ή αλλιώς το -50 που είδες.

Το 2ο πρόβλημα σου είναι ότι χρησιμοποιείς char. Η getchar σου επιστρέφει την τιμή 193 ή 206 ανάλογα αν μιλάμε για iso ή utf8. Αυτή η τιμή όμως δεν χωράει σε signed char οπότε "μεταφράζεται" στην αντίστοιχη αρνητική. Δηλαδή 193 - 256 μας δίνει -63 και αντίστοιχα 206 - 256 μας δίνει το -50 που παίρνεις. Θα μου πεις και γιατί με το %u δεν παίζει σωστά και εμφανίζει το τεράστιο νούμερο;

Αυτό οφείλεται για άλλη μία φορά στο γεγονός ότι χρησιμοποιείς char. Να θυμάσαι ότι στην C υπάρχει κάτι που λέγεται integer promotions δηλαδή καμμία πράξη δεν γίνεται με τύπο μικρότερο του int. Όταν δίνεις στην printf %d, %u, κτλ ο char που της δίνεις θα μετατραπεί σε int και εκείνη η τιμή είναι που τυπώνεται. Επειδή έχεις τύπο με πρόσημο (στην πλατφόρμα μας, ο char ισοδυναμεί στο περίπου με signed char), γίνεται το λεγόμενο sign extension δηλαδή κάθε παραπανίσιο bit θα συμπληρωθεί με το bit του προσήμου που στους αρνητικούς είναι 1.

Έτσι λοιπόν, ενώ εσύ νομίζεις ότι η printf τυπώνει τα 0xC1 ή 0xCE, στην πραγματικότητα τυπώνει τα 0xFFFFFFC1 και 0xFFFFFFCE λόγω της μετατροπής σε int. Όταν ερμηνεύσεις αυτά σαν τύπο με πρόσημο με την %d, παίρνεις τα -63 και -50. Όταν ερμηνεύσεις σαν τύπο χωρίς πρόσημο, τότε παίρνεις μια τεράστια 32bit τιμή 4δις και κάτι.

Αυτό λοιπόν που πρέπει να κάνεις είναι να ορίσεις την μεταβλητή σαν int. Αν όριζες την μεταβλητή σου σαν unsigned char αντί για σκέτο char, τότε δεν θα είχες αυτό το πρόβλημα αλλά θα είχες όμως άλλα προβλήματα.

Επεξ/σία από imitheos
  • Like 5

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

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

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

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

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

Σύνδεση

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

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