Si, dans une situation donnée, vous avez un tableau de caractères (se terminant bien sûr avec le caractère nul) et juste après, dans la position immédiatement suivante en mémoire, vous souhaitez stocker 0
sous la forme d'un entier non signé, comment l'ordinateur distingue-t-il ces deux?
Ce n'est pas.
Le terminateur de chaîne est un octet contenant tous les 0 bits.
Unsigned int est deux ou quatre octets (selon votre environnement) contenant chacun les 0 bits.
Les deux éléments 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 ont la même apparence 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 qui n'a de sens que pour le compilateur.
Edit-added: Par exemple: Il est parfaitement possible, même courant, d’effectuer une arithmétique sur les octets constituant une chaîne. Si vous avez une chaîne de caractères ASCII de 8 bits, vous pouvez convertir les lettres de la chaîne en majuscules et en minuscules en ajoutant ou en soustrayant 32 (décimal). Ou, si vous traduisez en un autre code de caractère, vous pouvez utiliser leurs valeurs en tant qu'indices dans un tableau dont les éléments fournissent le codage en bits équivalent dans l'autre code.
Pour le CPU, les caractères sont vraiment des entiers très courts. (huit bits chacun au lieu de 16, 32 ou 64). Pour nous, êtres humains, leurs valeurs sont associées à des caractères lisibles, mais le processeur n'en a aucune idée. Il ne sait pas non plus quoi que ce soit sur la convention "C" de "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). .
Certes, il existe certaines instructions dans x86/x64 qui ont tendance à être beaucoup utilisées avec des chaînes de caractères - le préfixe REP, par exemple - mais vous pouvez également les utiliser sur un tableau d'entiers, si elles donnent le résultat souhaité.
En bref, il n'y a pas de différence (sauf qu'un int a une largeur de 2 ou 4 octets et un caractère seulement 1).
Le fait est que toutes les bibliothèques modernes utilisent la technique du terminateur nul 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 lit un caractère nul ou qu'il a lu autant de caractères que sa taille l'indique.
Les problèmes avec ce début commencent quand le terminateur null est manquant ou la longueur est fausse car alors le programme commence à lire de la mémoire qu'il n'est pas censé.
Il n'y a pas de différence. Le code machine (assembleur) n'a pas de types de variables, mais le type des données est déterminé par l'instruction.
Un meilleur exemple serait int
et float
, si vous avez 4 octets en mémoire, il n'y a aucune information indiquant s'il s'agit d'une int
ou d'une float
(ou autre chose entièrement), mais il y a 2 instructions différentes pour l'addition entière et l'addition flottante, donc si l'instruction d'addition d'entier est utilisée sur les données, il s'agit d'un entier, et inversement.
Même chose avec les chaînes, si vous avez un code qui, par exemple, examine une adresse et compte les octets jusqu'à ce qu'il atteigne un octet \0
, vous pouvez le considérer comme une fonction calculant la longueur de la chaîne.
Bien sûr, une telle programmation serait une pure folie, c'est pourquoi nous avons des langages de plus haut niveau qui se compilent en code machine et presque aucun programme directement dans l'assembleur.
La seule réponse scientifique scientifique serait: métadonnées.
Les métadonnées indiquent à l'ordinateur si certaines données à un emplacement donné sont un entier, une chaîne, un code de programme ou autre. Ces métadonnées peuvent faire partie du code du programme (comme mentionné par Jamie Hanrahan) ou peuvent être explicitement stockées quelque part.
Les CPU modernes peuvent souvent faire la distinction entre 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 des chaînes et des nombres, oui. Mais le cas habituel est que le logiciel s’occupe de ce problème, qu’il s’agisse de métadonnées implicites (dans le code) ou explicites (les machines virtuelles orientées objet stockent souvent les métadonnées (informations de type/classe) dans les données (objet)). .
L’avantage de ne pas distinguer 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 réellement du code de programme, du texte lisible par l'homme ou des chiffres. Ce ne sont que des fragments qui sont transportés dans la machine. Laissez le code du programme traiter les problèmes de dactylographie sophistiqués.
Ce n'est pas. Tu le fais!
Ou votre compilateur/interprète.
Si les instructions indiquent à l’ordinateur d’ajouter le 0
en tant que nombre, il le fera. S'ils disent à l'ordinateur d'arrêter d'imprimer des données après avoir atteint le 0
, il le fera en tant que caractère \0'
.
Les langues disposent de mécanismes pour garantir le traitement des données. Les variables en C ont des types, tels que int
, float
et char
, et le compilateur génère des instructions correctes pour chaque type de données. Mais C vous permet de convertir les 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 des morceaux comme les autres.
Un caractère nul est un octet et un unsigned int est deux octets.