Kako možete odrediti razliku između ASCII u binarnom i istog decimalnog u binarnom obliku?


Odgovor 1:

Općenito, ne možete samo bita. Na primjer, broj 00111001 u binarnom obliku: to bi moglo biti broj 57, ali može biti i ASCII znamenka „9“.

To je rečeno, u praksi često možete reći razliku. Jer ćete imati neku ideju koja bi vrijednost trebala biti s kojom radite. Razmotrimo slijedeću C funkciju koja u sebi ima upadljivu pogrešku:

int print (int n) {char buf [1]; int i; i = 3 * n + 2; sprintf (buf, "% i \ n", i); stavlja (buf); vratiti i; }

Izračunava, za svaki cijeli broj n, vrijednost 3 * n + 2, tu vrijednost baca na konzolu i vraća vrijednost kao cijeli broj. Međutim, primijetili ćete prilikom testiranja ove funkcije da ako ulaz, recimo 9, na ispis ispisuje ispravan rezultat 29. Ali vratit će pogrešnu vrijednost, u ovom slučaju vrijednost 57. I to vam može dati nekakav trag o tome što se ovdje događa, jer ćete primijetiti da je 57 ASCII prikaz broja 9, a to se događa zadnja znamenka rezultata.

Zatim eksperimentirate i ustanovite da je to istina kad god je rezultat dvoznamenkasti broj. Primjerice, s n = 5 rezultat bi trebao biti 17, ali umjesto toga je 55, ASCII prikaz znamenke "7".

A kada rezultat ima više od dvije znamenke, rezultat je još čudniji. Na primjer, s n = 50, ispravan rezultat 152 baca se na konzolu, ali povratna vrijednost je 12853 u decimalnom obliku, ili 0x3235 u heksadecimalnoj vrijednosti. Možda ćete primijetiti da je to ASCII prikaz niza "25" ili posljednje dvije znamenke rezultata, obrnutim redoslijedom!

Dakle, što se ovdje događa? Primijetite da u međuspremniku znakova ima mjesta samo za jedan znak! Funkcija sprintf () u C-u ne provjerava prekoračenje međuspremnika, pa će rado zapisati svoj izlaz u memoriju na koju je ukazao buf, prepisujući bajtove odmah nakon bajtova rezerviranih za buf ako je buf premalen. U ovom su slučaju ovo bajtovi rezervirani za cijeli broj i i prebrisati se. A budući da se vrijednost i tada koristi kao povratna vrijednost ove funkcije, povratna vrijednost bit će netočna.

Ostaje samo jedno pitanje: zašto povratna vrijednost sadrži posljednje ASCII znamenke rezultata, ali obrnutim redoslijedom? To se događa jer (pod pretpostavkom da radite na PC-u) bajtovi cijelog broja pohranjuju se "pogrešno". Na primjer, 32-bitni cijeli broj 0x12345678 sprema se kao bajtovi 0x78 0x56 0x34 0x12 u memoriju.

Dakle, kada je ulaz n = 50, prva znamenka rezultata bit će spremljena u buf, dok će druga i treća znamenka rezultata završiti u i, koja će tada postati u bajtovima, 0x35 0x32 0x00 0x00. A to predstavlja vrijednost 0x3235 = 12853 u decimalnom obliku kada se tumači kao 32-bitni broj.

Kao zaključna napomena: ako ste to stvarno trebali isprobati na svom računalu, rezultati mogu biti različiti, jer učinci ove vrste bugova uvelike ovise o unutrašnjem radu vašeg stroja i vašem prevoditelju. Npr., Pametni telefon najčešće sprema svoje bajtove u ispravnom redoslijedu, pa ćete kao rezultat dobiti drugačiji broj. A vaš prevodilac može rezervirati više od 1 bajta za buf zbog problema s usklađivanjem memorije, ili može pohraniti buf i ja obrnuto (prvo u memoriju, a zatim buf). Ili se može optimizirati ako samo rezultat zadrži u registru CPU-a. U tom slučaju rezultat će biti točan, ali nešto drugo u sjećanju će biti oštećeno.

Općenito, ako programi sadrže greške poput ove, sve oklade odnose se na to što će se zapravo dogoditi.


Odgovor 2:

Ako je 48 ASCII prikaz broja nula, a 57 je ASCII prikaz devet, tada je najmanje značajan grickanje stvarna predstavljena znamenka:

0000 0000-0011 0000 = 32 + 16 + 0 = 48

0000 0001-0011 0001

0000 0010-0011 0010

0000 0011-0011 0011

0000 0100-0011 0100

0000 0101-0011 0101

0000 0110-0011 0110

0000 0111-0011 0111

0000 1000-0011 1000

0000 1001-0011 1001 = 32 + 16 + 8 + 1 = 57

ili jednostavno; oduzmite 48 da biste dobili broj.