Як можна визначити різницю між ASCII у двійковій і однаковою десятковою у двійковій?


Відповідь 1:

Взагалі, ви не можете, не по шматочках поодинці. Наприклад, число 00111001 у двійковій формі: це може бути число 57, але це також може бути цифра ASCII “9”.

Зважаючи на це, на практиці часто можна сказати різницю. Тому що ви матимете деяке уявлення про те, яке значення має бути, з яким ви працюєте. Розглянемо наступну функцію C, в якій є яскравий помилка:

int print (int n) {char buf [1]; int i; i = 3 * n + 2; sprintf (buf, "% i \ n", i); ставить (буф); повернути i; }

Він обчислює для кожного цілого n значення 3 * n + 2, скидає це значення на консоль і повертає значення як ціле число. Однак ви можете помітити при тестуванні цієї функції, що якщо вхід, скажімо, 9, він виводить правильний результат 29 на консоль. Але воно поверне неправильне значення, в цьому випадку значення 57. І це може дати вам деяку підказку щодо того, що відбувається тут, оскільки ви помітите, що 57 - це представлення ASCII числа 9, і це відбувається остання цифра результату.

Потім ви проводите експерименти і виявляєте, що це дійсно, коли результат є двозначним числом. Наприклад, при n = 5 результат повинен бути 17, але замість цього результат є 55, ASCII подання цифри "7".

А коли результат має більше двох цифр, результат стає ще більш дивний. Наприклад, при n = 50 правильний результат 152 скидається на консоль, але повернене значення - 12853 в десятковій або 0x3235 у шістнадцятковій. Ви можете помітити, що це представлення ASCII рядка "25" або дві останні цифри результату у зворотному порядку!

Отже, що тут відбувається? Зауважте, що буфер символів має місце лише для одного символу! Функція sprintf () в C не перевіряє наявність перевищення буфера, тому вона з радістю запише свій вихід у пам'ять, на яку вказує buf, перезаписуючи байти відразу після байтів, зарезервованих для buf, якщо buf занадто малий. У цьому випадку це байти, зарезервовані для цілого числа i, і вони перезаписуються. І оскільки значення i потім використовується як повернене значення цієї функції, значення повернення буде неправильним.

Залишається лише одне питання: чому повернене значення містить останні ASCII цифри результату, але у зворотному порядку? Це трапляється тому, що (припускаючи, що ви працюєте на ПК), байти цілого числа зберігаються "неправильно". Наприклад, 32-бітове ціле число 0x12345678 зберігається у байтах 0x78 0x56 0x34 0x12 в пам'яті.

Отже, коли вхід n = 50, перша цифра результату буде зберігатися в buf, а друга і третя цифри результату закінчуються в i, яка потім стане в байтах 0x35 0x32 0x00 0x00. І це представляє значення 0x3235 = 12853 у десятковій частині, коли інтерпретується як 32-бітове число.

Як підсумкове зауваження: якщо ви насправді спробували це на своїй машині, результати можуть бути різними, оскільки наслідки помилок цього типу сильно залежать від внутрішніх функцій вашої машини та вашого компілятора. Наприклад, смартфон найчастіше буде зберігати свої байти в правильному порядку, тому в результаті ви отримаєте інше число. І ваш компілятор цілком може зарезервувати більше 1 байта для buf через проблеми з вирівнюванням пам’яті, або він може зберігати buf та i навпаки (я спочатку в пам’яті, потім buf). Або це може оптимізувати i away, лише зберігаючи результат у реєстрі CPU. У такому випадку результат буде правильним, але щось інше в пам'яті буде пошкоджено.

Загалом, якщо програми містять подібні помилки, всі ставки стосуються того, що буде насправді.


Відповідь 2:

Якщо 48 - це представлення числа ASCII нуля, а 57 - представлення ASCII дев'яти, то найменш значущим прикусом є фактично представлена ​​цифра:

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

або просто; відніміть 48, щоб дати вам число.