Як комп’ютер відрізняє "\ 0" (нульовий символ) від "unsigned int = 0"?


29

Якщо в даній ситуації у вас є масив знаків (що закінчується, звичайно, нульовим символом), і відразу після цього, в найближчій наступній позиції в пам'яті, ви хочете зберігати 0як неподписаний int, як комп'ютер відрізняє ці два?


18
Ви запитуєте про типові комп’ютери, на які відповіді цілком вірні. Однак раніше існували деякі архітектури, які використовують теговану пам'ять для розмежування типів даних.
grawity

12
Таким же чином комп'ютер не може диференціювати 4-байтний поплавок від цілого цілого числа 4 байтів (повторне представлення зовсім іншого числа).
Хаген фон Ейтцен

6
Хоча закінчення рядка на 0x00 є загальним, є мови, які використовують рядки з префіксом довжини. Перший байт або два містять кількість байтів у рядку. Таким чином, 0x00 в кінці не потрібен. Мені здається, я пригадую, що Паскаль і БАЗИК робили це. Можливо, і COBOL.
освітлено

Формати заголовків @lit також у багатьох протоколах зв'язку. "Здрастуйте, я таке повідомлення, і мені так багато байтів". Часто тому, що вам потрібно зберігати складні типи даних всередині, тоді нульове припинення стає набагато складнішим для розбору.
mathreadler

1
@lit: Більшість варіантів Pascal і BASIC так, і PL / I, і Ada - і в Java, оскільки обмін підрядками було скинуто в 7u6, ефективно використовує префікс довжини масиву - але це лише COBOL: ви можете читати дані з pic X occurs m to n depending on v( і рахувати можна де завгодно, не тільки безпосередньо перед цим), але зберігати його складніше.
dave_thompson_085

Відповіді:


86

Це не так.

Термінальний рядок - це байт, що містить усі 0 біт.

Непідписаний int - це два або чотири байти (залежно від вашого оточення), кожен з яких містить усі 0 біт.

Два елементи зберігаються за різними адресами. Скомпільований код виконує операції, придатні для рядків на колишньому місці розташування, і операції, придатні для непідписаних двійкових чисел на другому. (Якщо у вас немає або помилки у вашому коді, чи якогось небезпечно розумного коду!)

Але всі ці байти виглядають однаково на процесорі. Дані в пам'яті (у більшості поширених на даний момент архітектурних наборів інструкцій) не мають асоційованого типу. Це абстракція, яка існує лише у вихідному коді і означає щось лише для компілятора.

Правка додана: Як приклад: Цілком можливо, навіть звичайно, виконувати арифметику на байтах, що складають рядок. Якщо у вас є рядок з 8-бітових символів ASCII, ви можете перетворити літери в рядку між верхнім і малим регістром, додаючи або віднімаючи 32 (десятковий). Або якщо ви перекладаєте на інший символьний код, ви можете використовувати їх значення як індекси в масив, елементи якого забезпечують еквівалентне бітове кодування в іншому коді.

Для процесора символи дійсно є надзвичайно короткими цілими числами. (вісім біт кожен замість 16, 32 або 64.) Для нас, люди, їх значення, можливо, асоціюються з читабельними символами, але процесор цього поняття не має. Він також нічого не знає про умову "С" "нульовий байт закінчує рядок" (і як багато хто зазначав в інших відповідях і коментарях, існують середовища програмування, в яких ця конвенція взагалі не використовується) .

Напевно, в x86 / x64 є деякі інструкції, які, як правило, використовуються багато з рядками - наприклад, префікс REP - але ви також можете добре використовувати їх у масиві цілих чисел, якщо вони досягнуть бажаного результату.


14
Ось чому розробникам доводиться бути обережними з рядками. Якщо у вас, скажімо, 100 послідовних байтів, ви можете помістити максимум 99 1-байтних символів плюс термінатор в останньому байті. Якщо ви запишете туди 100-байтовий рядок, програма не зможе зрозуміти, що рядок закінчується там, і продовжить читання послідовних байтів до випадкового нульового байта. Якщо рядок довжиною більше 100 байт, він замінить деякі суміжні дані. Мови програмування високого рівня (Java, C #, JS та ін.) Самі про це подбають, але в мовниках низького рівня, таких як C, C ++, складання - це відповідальність розробника.
gronostaj

18
@gronostaj Ваш коментар трохи заплутаний: на відміну від C, рядки C ++ також про це подбають автоматично. C ++ також взагалі не класифікується як мова низького рівня (і навіть C іноді це не так).
Конрад Рудольф

5
Існують (старі) архітектури процесора, які мають маркери типів на значеннях даних, тому вилучення цілого числа як вказівника дасть виняток.
Саймон Ріхтер

8
@JamieHanrahan Процесор IA64 має біт, який називається NaT (або "Не річ"), який може кинути виняток, якщо для нього встановлено значення.
ErikF

4
@KonradRudolph "автоматичний" не означає "дурний", звичайно, не на C ++
rackandboneman

5

Коротше кажучи, різниці немає (за винятком того, що інт шириною 2 або 4 байти, а знаком всього 1).

Вся справа в тому, що всі сучасні бібліотеки або використовують техніку нульового термінатора, або зберігають довжину струни. І в обох випадках програма / комп’ютер знає, що вона досягла кінця рядка, коли вона прочитала нульовий символ або прочитала стільки символів, скільки розмір підказує.

Проблеми з цим запуском, коли нульовий термінатор відсутній або довжина неправильна, оскільки програма починає читати з пам'яті, якої не слід.


3
О, різниця є в короткому - насправді, короткий - це щось горезвісне, оскільки дуже тип машини залежить від машини :)
rackandboneman

2

Різниці немає. Машинний код (ассемблер) не має змінних типів, натомість тип даних визначається інструкцією.

Кращим прикладом може бути, intі floatякщо у вас є 4 байти в пам’яті, немає інформації про те, intчи це float(або щось інше цілком), проте є дві різні інструкції щодо додавання цілого чи плаваючого додавання, так що якщо додавання цілого числа інструкція використовується на даних, тоді це ціле число, і навпаки.

Як і у рядків, якщо у вас є код, який, скажімо, переглядає адресу і рахує байти, поки не досягне \0байта, ви можете вважати це функцією, що обчислює довжину рядка.

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


2

Наукова відповідь на одне слово буде: метадані.

Метадані повідомляє комп’ютеру, чи є деякі дані в певному місці int, рядок, програмний код чи інше. Ці метадані можуть бути частиною програмного кодексу (як згадував Джеймі Ханрахан) або можуть бути явно десь збережені.

Сучасні процесори часто можуть розрізняти регіони пам'яті, призначені програмному коду, та регіони даних (наприклад, біт NX https://en.wikipedia.org/wiki/NX_bit ). Деякі екзотичні засоби також можуть розрізняти рядки та цифри, так. Але звичайний випадок полягає в тому, що Програмне забезпечення опікується цією проблемою, хоч неявні метадані (в коді) або явні метадані (об'єктно-орієнтовані віртуальні машини часто зберігають метадані (інформацію про тип / клас) як частину даних (об’єкт)) .

Перевагою не розрізняти різні види даних є те, що деякі операції стають дуже простими. Підсистемі вводу-виводу не обов'язково потрібно знати, чи є дані, які він просто читає з або записує на диск, насправді програмний код, читаний людиною текст або цифри. Це всього лише біти, які транспортуються через машину. Дозвольте програмному коду вирішувати модні проблеми введення тексту.


0

Це не так. Ви робите це!

Або ваш упорядник / перекладач.

Якщо інструкція підкаже комп'ютеру додати число 0як число, воно зробить це. Якщо вони скажуть комп’ютеру припинити друкувати дані після досягнення 0, як " \0'char", це зробить це.

Мови мають механізми, що забезпечують поводження з даними. У змінні C є типи, як int, floatі char, і компілятор генерує правильні інструкції для кожного типу даних. Але C дозволяє передавати дані зі змінної в іншу змінну іншого типу, навіть вказівник на який можна використовувати як число. Для комп'ютера це все біти, як і будь-який інший.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.