Comment un ordinateur différencie-t-il «\ 0» (caractère nul) de «unsigned int = 0»?

29

Si dans une situation donnée, vous avez un tableau de caractères (se terminant bien sûr par le caractère nul) et juste après, dans la position immédiatement suivante en mémoire, vous souhaitez stocker 0en tant qu'int non signé, comment l'ordinateur fait-il la différence entre ces deux?

Angelixus
la source
18
Vous demandez des ordinateurs typiques sur lesquels les réponses sont tout à fait correctes. Cependant, certaines architectures utilisaient la mémoire balisée pour distinguer les types de données.
grawity
12
De la même manière, l'ordinateur ne peut pas différencier un flottant de 4 octets d'un entier de 4 octets (représentant à nouveau un nombre très différent).
Hagen von Eitzen
6
Bien que la fin d'une chaîne avec 0x00 soit courante, il existe des langages qui utilisent des chaînes à préfixe de longueur. Le premier octet ou deux contiendrait le nombre d'octets dans la chaîne. De cette façon, un 0x00 à la fin n'est pas nécessaire. Il me semble que Pascal et BASIC l'ont fait. Peut-être aussi COBOL.
allumé
@lit également des formats d'en-tête dans de nombreux protocoles de communication. "Bonjour, je suis ce genre de message et je fais autant d'octets". Souvent, parce que vous devez stocker des types de données complexes à l'intérieur, la terminaison nulle devient beaucoup plus difficile à analyser.
mathreadler
1
@lit: La plupart des variantes de Pascal et BASIC oui, et PL / I et Ada - et en Java depuis que le partage de sous-chaîne a été abandonné dans 7u6 utilise effectivement le préfixe de longueur de tableau - mais COBOL uniquement une sorte de tri: vous pouvez lire les données de pic X occurs m to n depending on v( et le décompte peut être n'importe où, pas juste avant), mais le stocker est plus compliqué.
dave_thompson_085

Réponses:

86

Ce n'est pas le cas.

Le terminateur de chaîne est un octet contenant tous les 0 bits.

L'int entier non signé est de deux ou quatre octets (selon votre environnement) contenant chacun les 0 bits.

Les deux articles sont stockés à des adresses différentes. Votre code compilé effectue des opérations adaptées aux chaînes sur le premier emplacement et des opérations adaptées aux nombres binaires non signés sur le dernier. (Sauf si vous avez soit un bug dans votre code, soit un code dangereusement intelligent!)

Mais tous ces octets sont identiques pour le processeur. Les données en mémoire (dans la plupart des architectures de jeux d'instructions courantes) ne sont associées à aucun type. C'est une abstraction qui n'existe que dans le code source et ne signifie que pour le compilateur.

Édition ajoutée: à titre d'exemple: il est parfaitement possible, même courant, d'effectuer une arithmétique sur les octets qui composent une chaîne. Si vous avez une chaîne de caractères ASCII 8 bits, vous pouvez convertir les lettres de la chaîne entre les majuscules et les minuscules en ajoutant ou en soustrayant 32 (décimal). Ou si vous traduisez vers un autre code de caractère, vous pouvez utiliser leurs valeurs comme indices dans un tableau dont les éléments fournissent le codage binaire équivalent dans l'autre code.

Pour le CPU, les caractères sont vraiment des entiers extra-courts. (huit bits chacun au lieu de 16, 32 ou 64.) Pour nous, les humains, leurs valeurs sont associées à des caractères lisibles, mais le CPU n'en a aucune idée. Il ne sait rien non plus sur la convention "C" de "l'octet nul termine une chaîne" (et comme beaucoup l'ont noté dans d'autres réponses et commentaires, il existe des environnements de programmation dans lesquels cette convention n'est pas utilisée du tout) .

Certes, certaines instructions en x86 / x64 sont généralement utilisées avec des chaînes - le préfixe REP par exemple - mais vous pouvez tout aussi bien les utiliser sur un tableau d'entiers, si elles atteignent le résultat souhaité.

Jamie Hanrahan
la source
14
C'est pourquoi les développeurs doivent faire attention aux chaînes. Si vous avez, disons, 100 octets consécutifs, vous pouvez contenir au maximum 99 caractères de 1 octet plus le terminateur dans le dernier octet. Si vous y écrivez une chaîne de 100 octets, le programme ne pourra pas comprendre que la chaîne s'arrête là et continuera à lire des octets consécutifs jusqu'à un octet zéro coïncident. Si la chaîne fait plus de 100 octets, elle écrasera certaines données adjacentes. Les langages de programmation de haut niveau (Java, C #, JS, etc.) s'en occupent eux-mêmes, mais dans les langages de bas niveau tels que C, C ++, l'assemblage, c'est la responsabilité du développeur.
gronostaj
18
@gronostaj Votre commentaire est légèrement déroutant: contrairement au C, les chaînes C ++ s'en occupent également automatiquement. C ++ n'est pas non plus généralement classé comme un langage de bas niveau (et même C parfois ne l'est pas).
Konrad Rudolph
5
Il existe des (anciennes) architectures CPU qui ont des marqueurs de type sur les valeurs de données, donc le déréférencement d'un entier comme pointeur donnera une exception.
Simon Richter
8
@JamieHanrahan Le processeur IA64 a un bit appelé NaT (ou "Not a Thing") qui peut lever une exception si une valeur est définie.
ErikF
4
@KonradRudolph "automatique" ne signifie pas "infaillible", certainement pas en C ++
rackandboneman
5

En bref, il n'y a pas de différence (sauf qu'un int fait 2 ou 4 octets de large et un char juste 1).

Le fait est que toutes les bibliothèques modernes utilisent la technique de terminaison nulle ou stockent la longueur d'une chaîne. Et dans les deux cas, le programme / ordinateur sait qu'il a atteint la fin d'une chaîne lorsqu'il a lu un caractère nul ou qu'il a lu autant de caractères que la taille le lui indique.

Problèmes avec ce démarrage lorsque le terminateur nul est manquant ou que la longueur est incorrecte, car alors le programme commence à lire à partir de la mémoire, il n'est pas censé le faire.

BrainStone
la source
3
Oh, il y a une différence en bref - en fait, court est un peu connu pour être un type de données très dépendant de la machine :)
rackandboneman
2

Il n'y a pas de différence. Le code machine (assembleur) n'a pas de types variables, mais le type des données est déterminé par l'instruction.

Un meilleur exemple serait intet float, si vous avez 4 octets en mémoire, il n'y a aucune information quant à savoir si c'est un intou un float(ou quelque chose d'autre entièrement), mais il y a 2 instructions différentes pour l'addition entière et l'addition flottante, donc si l'ajout entier l'instruction est utilisée sur les données, alors c'est un entier, et vice versa.

De même pour les chaînes, si vous avez du code qui, par exemple, regarde une adresse et compte les octets jusqu'à ce qu'elle atteigne un \0octet, vous pouvez la considérer comme une fonction calculant la longueur de la chaîne.

Bien sûr, une programmation comme celle-ci serait une folie complète, c'est pourquoi nous avons des langages de niveau supérieur qui compilent directement du code machine et presque personne dans l'assembleur.

kajacx
la source
2

La réponse scientifique en un seul mot serait: métadonnées.

Les métadonnées indiquent à l'ordinateur si certaines données à un certain emplacement sont un entier, une chaîne, un code de programme ou autre. Ces métadonnées peuvent faire partie du code du programme (comme l'a mentionné Jamie Hanrahan) ou elles peuvent être explicitement stockées quelque part.

Les processeurs modernes peuvent souvent distinguer les régions de mémoire affectées au code de programme et les régions de données (par exemple, le bit NX https://en.wikipedia.org/wiki/NX_bit ). Certains matériels exotiques peuvent également faire la distinction entre les chaînes et les nombres, oui. Mais le cas habituel est que le logiciel s'occupe de ce problème, que ce soit par des métadonnées implicites (dans le code) ou des métadonnées explicites (les machines virtuelles orientées objet stockent souvent les métadonnées (informations de type / classe) dans le cadre des données (objet)) .

Un avantage de ne pas faire la distinction entre différents types de données est que certaines opérations deviennent très simples. Le sous-système d'E / S n'a pas nécessairement besoin de savoir si les données qu'il lit ou écrit sur le disque sont en fait du code de programme, du texte lisible par l'homme ou des nombres. Ce ne sont que des morceaux qui sont transportés à travers la machine. Laissez le code du programme gérer les problèmes de frappe fantaisie.

Klaws
la source
0

Ce n'est pas le cas. Tu le fais!

Ou votre compilateur / interprète.

Si les instructions indiquent à l'ordinateur d'ajouter le 0sous forme de nombre, il le fera. S'ils disent à l'ordinateur d'arrêter d'imprimer les données après avoir atteint le 0, en tant que «caractère» \0', il le fera.

Les langues ont des mécanismes pour garantir la façon de traiter les données. En C, les variables ont des types, comme int, floatet char, et le compilateur génère les bonnes instructions pour chaque type de données. Mais C vous permet de convertir des données d'une variable en une autre variable de type différent, même un pointeur peut être utilisé comme un nombre. Pour l'ordinateur, ce sont tous des bits comme les autres.

carlos prado
la source
0

Un caractère nul est un octet et un entier non signé est deux octets.

Quentin 2
la source