W jaki sposób komputer odróżnia „\ 0” (znak zerowy) od „unsigned int = 0”?
29
Jeśli w danej sytuacji masz tablicę znaków (kończącą się oczywiście znakiem null), a zaraz potem, w bezpośredniej następnej pozycji w pamięci, chcesz zapisać 0jako int bez znaku, w jaki sposób komputer rozróżnia je dwa?
Pytasz o typowe komputery, o których odpowiedzi są całkowicie poprawne. Jednak kiedyś istniały pewne architektury, które używają znaczników pamięci do rozróżnienia typów danych.
grawity
12
W ten sam sposób, w jaki komputer nie może odróżnić 4-bajtowej liczby zmiennoprzecinkowej od 4-bajtowej liczby całkowitej (reprezentując zupełnie inną liczbę).
Hagen von Eitzen
6
Chociaż kończenie łańcucha na 0x00 jest powszechne, istnieją języki, które używają ciągów z prefiksem długości. Pierwszy lub dwa bajty zawierałyby liczbę bajtów w ciągu. W ten sposób 0x00 na końcu nie jest potrzebny. Wydaje mi się, że Pascal i BASIC to robią. Być może również COBOL.
zapalił
@lit również formaty nagłówków w wielu protokołach komunikacyjnych. „Cześć, jestem tego rodzaju wiadomością i mam tyle bajtów”. Często ponieważ musisz przechowywać w środku złożone typy danych, zakończenie zerowania staje się znacznie trudniejsze do przeanalizowania.
mathreadler
1
@lit: Większość wariantów Pascala i BASICa tak, a PL / I i Ada - i w Javie, ponieważ udostępnianie podłańcuchów zostało usunięte w 7u6, skutecznie używa prefiksu długości tablicy - ale COBOL jest tylko w pewnym sensie: możesz odczytać dane z pic X occurs m to n depending on v( a liczba może być dowolna, nie tylko bezpośrednio wcześniej), ale przechowywanie jej jest bardziej skomplikowane.
dave_thompson_085
Odpowiedzi:
86
Nie ma
Terminator łańcucha jest bajtem zawierającym wszystkie 0 bitów.
Bez znaku int ma dwa lub cztery bajty (w zależności od środowiska), z których każdy zawiera wszystkie 0 bitów.
Te dwa elementy są przechowywane pod różnymi adresami. Skompilowany kod wykonuje operacje odpowiednie dla łańcuchów w pierwszej lokalizacji, a operacje odpowiednie dla niepodpisanych liczb binarnych w drugiej. (Chyba że masz błąd w kodzie lub niebezpiecznie sprytny kod!)
Ale wszystkie te bajty wyglądają tak samo dla procesora. Dane w pamięci (w najbardziej popularnych obecnie architekturach zestawów instrukcji) nie są z nimi powiązane. To abstrakcja, która istnieje tylko w kodzie źródłowym i oznacza coś tylko dla kompilatora.
Dodane do edycji: Na przykład: arytmetyka bajtów tworzących ciąg znaków jest nawet całkiem powszechna. Jeśli masz ciąg 8-bitowych znaków ASCII, możesz przekonwertować litery w łańcuchu między dużymi i małymi literami, dodając lub odejmując 32 (dziesiętnie). Lub jeśli tłumaczysz na inny kod znakowy, możesz użyć ich wartości jako indeksów w tablicy, której elementy zapewniają równoważne kodowanie bitów w innym kodzie.
Dla procesora znaki są bardzo krótkimi liczbami całkowitymi. (osiem bitów zamiast 16, 32 lub 64.) Dla nas ludzi ich wartości są powiązane z czytelnymi znakami, ale CPU nie ma o tym pojęcia. Nie wie też nic o konwencji „C” „bajt zerowy kończy ciąg znaków” (i jak wielu zauważyło w innych odpowiedziach i komentarzach, istnieją środowiska programistyczne, w których ta konwencja w ogóle nie jest używana) .
Dla pewności, istnieją pewne instrukcje w x86 / x64, które zwykle są często używane z łańcuchami - na przykład przedrostek REP - ale równie dobrze możesz ich użyć na tablicy liczb całkowitych, jeśli osiągną pożądany wynik.
Dlatego programiści muszą uważać na łańcuchy. Jeśli masz, powiedzmy, 100 kolejnych bajtów, możesz zmieścić maksymalnie 99 1-bajtowych znaków plus terminator w ostatnim bajcie. Jeśli napiszesz tam łańcuch 100-bajtowy, program nie będzie w stanie stwierdzić, że łańcuch się tam kończy i będzie kontynuował czytanie kolejnych bajtów, aż do przypadkowego zera bajtu. Jeśli ciąg ma więcej niż 100 bajtów, zastąpi niektóre sąsiednie dane. Językami programowania wysokiego poziomu (Java, C #, JS itp.) Zajmują się tym sami, ale w językach niskiego poziomu, takich jak C, C ++, asembler jest odpowiedzialny za tworzenie.
gronostaj
18
@gronostaj Twój komentarz jest nieco mylący: W przeciwieństwie do C, łańcuchy C ++ również zajmują się tym automatycznie. C ++ również ogólnie nie jest klasyfikowany jako język niskiego poziomu (a czasem nawet C nie jest).
Konrad Rudolph
5
Istnieją (stare) architektury procesorów, które mają znaczniki typu na wartościach danych, więc odłożenie liczby całkowitej jako wskaźnika da wyjątek.
Simon Richter
8
@JamieHanrahan Procesor IA64 ma bit o nazwie NaT (lub „Not a Thing”), który może zgłosić wyjątek, jeśli ustawiono wartość.
ErikF
4
@KonradRudolph „automatyczny” nie oznacza „niezawodny”, na pewno nie w C ++
rackandboneman
5
Krótko mówiąc, nie ma różnicy (z wyjątkiem tego, że int ma szerokość 2 lub 4 bajtów, a char tylko 1).
Chodzi o to, że wszystkie współczesne biblioteki lib używają techniki zerowego terminatora lub przechowują długość łańcucha. I w obu przypadkach program / komputer wie, że osiągnął koniec łańcucha, gdy albo czyta pusty znak, albo przeczytał tyle znaków, ile nakazuje rozmiar.
Problemy z tym uruchomieniem, gdy brakuje terminatora zerowego lub jego długość jest niepoprawna, ponieważ wtedy program zaczyna czytać z pamięci, czego nie powinien.
Och, istnieje różnica w skrócie - w rzeczywistości skrót jest dość znany z tego, że jest bardzo zależnym od maszyny typem danych :)
rackandboneman
2
Nie ma różnicy. Kod maszynowy (asembler) nie ma typów zmiennych, zamiast tego typ danych jest określony przez instrukcję.
Lepszym przykładem byłoby, inta floatjeśli masz 4 bajty w pamięci, nie ma informacji o tym, czy jest to a intlub float(lub coś zupełnie innego), jednak istnieją 2 różne instrukcje dotyczące dodawania liczb całkowitych i liczb zmiennoprzecinkowych, więc jeśli dodawanie liczb całkowitych instrukcja jest używana na danych, następnie jest liczbą całkowitą i odwrotnie.
Podobnie z łańcuchami, jeśli masz kod, który, powiedzmy, patrzy na adres i zlicza bajty, aż osiągnie \0bajt, możesz myśleć o nim jako o funkcji obliczającej długość łańcucha.
Oczywiście takie programowanie byłoby kompletnym szaleństwem, dlatego mamy języki wyższego poziomu, które kompilują się do kodu maszynowego i prawie żadnych programów bezpośrednio w asemblerze.
Naukowa odpowiedź na jedno słowo brzmiałaby: metadane.
Metadane informują komputer, czy niektóre dane w określonej lokalizacji to int, ciąg, kod programu lub cokolwiek innego. Te metadane mogą być częścią kodu programu (jak wspomniał Jamie Hanrahan) lub mogą być gdzieś jawnie zapisane.
Nowoczesne procesory często potrafią rozróżnić regiony pamięci przypisane do kodu programu i regionów danych (na przykład NX Bit https://en.wikipedia.org/wiki/NX_bit ). Niektóre egzotyczne urządzenia mogą również rozróżniać ciągi i liczby, tak. Zwykle jednak oprogramowanie zajmuje się tym problemem, choć metadane niejawne (w kodzie) lub metadane jawne (maszyny wirtualne zorientowane obiektowo często przechowują metadane (informacje o typie / klasie) jako część danych (obiektu)) .
Zaletą nierozróżniania różnych rodzajów danych jest to, że niektóre operacje stają się bardzo proste. Podsystem we / wy niekoniecznie musi wiedzieć, czy dane, które po prostu odczytuje lub zapisuje na dysk, to w rzeczywistości kod programu, tekst lub liczby czytelne dla człowieka. To wszystko tylko kawałki, które są transportowane przez maszynę. Niech kod programu poradzi sobie z fantazyjnymi problemami z pisaniem.
Jeśli instrukcje powiedzą komputerowi, aby dodać 0jako liczbę, zrobi to. Jeśli mówią komputer przestanie drukować dane po osiągną 0jako ' \0'char, to będzie to zrobić.
Języki mają mechanizmy zapewniające sposób przetwarzania danych. W C zmienne mają typów, takich jak int, floati char, i kompilator wygeneruje odpowiednie instrukcje dla każdego rodzaju danych. Ale C pozwala rzutować dane ze zmiennej na inną zmienną różnego typu, nawet wskaźnik, którego można użyć jako liczby. Do komputera to wszystko jak każdy inny.
pic X occurs m to n depending on v
( a liczba może być dowolna, nie tylko bezpośrednio wcześniej), ale przechowywanie jej jest bardziej skomplikowane.Odpowiedzi:
Nie ma
Terminator łańcucha jest bajtem zawierającym wszystkie 0 bitów.
Bez znaku int ma dwa lub cztery bajty (w zależności od środowiska), z których każdy zawiera wszystkie 0 bitów.
Te dwa elementy są przechowywane pod różnymi adresami. Skompilowany kod wykonuje operacje odpowiednie dla łańcuchów w pierwszej lokalizacji, a operacje odpowiednie dla niepodpisanych liczb binarnych w drugiej. (Chyba że masz błąd w kodzie lub niebezpiecznie sprytny kod!)
Ale wszystkie te bajty wyglądają tak samo dla procesora. Dane w pamięci (w najbardziej popularnych obecnie architekturach zestawów instrukcji) nie są z nimi powiązane. To abstrakcja, która istnieje tylko w kodzie źródłowym i oznacza coś tylko dla kompilatora.
Dodane do edycji: Na przykład: arytmetyka bajtów tworzących ciąg znaków jest nawet całkiem powszechna. Jeśli masz ciąg 8-bitowych znaków ASCII, możesz przekonwertować litery w łańcuchu między dużymi i małymi literami, dodając lub odejmując 32 (dziesiętnie). Lub jeśli tłumaczysz na inny kod znakowy, możesz użyć ich wartości jako indeksów w tablicy, której elementy zapewniają równoważne kodowanie bitów w innym kodzie.
Dla procesora znaki są bardzo krótkimi liczbami całkowitymi. (osiem bitów zamiast 16, 32 lub 64.) Dla nas ludzi ich wartości są powiązane z czytelnymi znakami, ale CPU nie ma o tym pojęcia. Nie wie też nic o konwencji „C” „bajt zerowy kończy ciąg znaków” (i jak wielu zauważyło w innych odpowiedziach i komentarzach, istnieją środowiska programistyczne, w których ta konwencja w ogóle nie jest używana) .
Dla pewności, istnieją pewne instrukcje w x86 / x64, które zwykle są często używane z łańcuchami - na przykład przedrostek REP - ale równie dobrze możesz ich użyć na tablicy liczb całkowitych, jeśli osiągną pożądany wynik.
źródło
Krótko mówiąc, nie ma różnicy (z wyjątkiem tego, że int ma szerokość 2 lub 4 bajtów, a char tylko 1).
Chodzi o to, że wszystkie współczesne biblioteki lib używają techniki zerowego terminatora lub przechowują długość łańcucha. I w obu przypadkach program / komputer wie, że osiągnął koniec łańcucha, gdy albo czyta pusty znak, albo przeczytał tyle znaków, ile nakazuje rozmiar.
Problemy z tym uruchomieniem, gdy brakuje terminatora zerowego lub jego długość jest niepoprawna, ponieważ wtedy program zaczyna czytać z pamięci, czego nie powinien.
źródło
Nie ma różnicy. Kod maszynowy (asembler) nie ma typów zmiennych, zamiast tego typ danych jest określony przez instrukcję.
Lepszym przykładem byłoby,
int
afloat
jeśli masz 4 bajty w pamięci, nie ma informacji o tym, czy jest to aint
lubfloat
(lub coś zupełnie innego), jednak istnieją 2 różne instrukcje dotyczące dodawania liczb całkowitych i liczb zmiennoprzecinkowych, więc jeśli dodawanie liczb całkowitych instrukcja jest używana na danych, następnie jest liczbą całkowitą i odwrotnie.Podobnie z łańcuchami, jeśli masz kod, który, powiedzmy, patrzy na adres i zlicza bajty, aż osiągnie
\0
bajt, możesz myśleć o nim jako o funkcji obliczającej długość łańcucha.Oczywiście takie programowanie byłoby kompletnym szaleństwem, dlatego mamy języki wyższego poziomu, które kompilują się do kodu maszynowego i prawie żadnych programów bezpośrednio w asemblerze.
źródło
Naukowa odpowiedź na jedno słowo brzmiałaby: metadane.
Metadane informują komputer, czy niektóre dane w określonej lokalizacji to int, ciąg, kod programu lub cokolwiek innego. Te metadane mogą być częścią kodu programu (jak wspomniał Jamie Hanrahan) lub mogą być gdzieś jawnie zapisane.
Nowoczesne procesory często potrafią rozróżnić regiony pamięci przypisane do kodu programu i regionów danych (na przykład NX Bit https://en.wikipedia.org/wiki/NX_bit ). Niektóre egzotyczne urządzenia mogą również rozróżniać ciągi i liczby, tak. Zwykle jednak oprogramowanie zajmuje się tym problemem, choć metadane niejawne (w kodzie) lub metadane jawne (maszyny wirtualne zorientowane obiektowo często przechowują metadane (informacje o typie / klasie) jako część danych (obiektu)) .
Zaletą nierozróżniania różnych rodzajów danych jest to, że niektóre operacje stają się bardzo proste. Podsystem we / wy niekoniecznie musi wiedzieć, czy dane, które po prostu odczytuje lub zapisuje na dysk, to w rzeczywistości kod programu, tekst lub liczby czytelne dla człowieka. To wszystko tylko kawałki, które są transportowane przez maszynę. Niech kod programu poradzi sobie z fantazyjnymi problemami z pisaniem.
źródło
Nie ma Ty to zrób!
Lub twój kompilator / tłumacz.
Jeśli instrukcje powiedzą komputerowi, aby dodać
0
jako liczbę, zrobi to. Jeśli mówią komputer przestanie drukować dane po osiągną0
jako '\0'
char, to będzie to zrobić.Języki mają mechanizmy zapewniające sposób przetwarzania danych. W C zmienne mają typów, takich jak
int
,float
ichar
, i kompilator wygeneruje odpowiednie instrukcje dla każdego rodzaju danych. Ale C pozwala rzutować dane ze zmiennej na inną zmienną różnego typu, nawet wskaźnik, którego można użyć jako liczby. Do komputera to wszystko jak każdy inny.źródło
Znak null to jeden bajt, a bez znaku int to dwa bajty.
źródło