web-dev-qa-db-fra.com

Comment les types de données C sont-ils «pris en charge directement par la plupart des ordinateurs»?

Je lis K&R "Le langage de programmation C" et suis tombé sur cette déclaration [Introduction, p. 3]:

Étant donné que les types de données et les structures de contrôle fournis par C sont pris en charge directement par la plupart des ordinateurs, la bibliothèque d'exécution requise pour implémenter des programmes autonomes est minuscule.

Que signifie la déclaration en gras? Existe-t-il un exemple de type de données ou de structure de contrôle qui n'est pas pris en charge directement par un ordinateur?

114
gwg

Oui, certains types de données ne sont pas directement pris en charge.

Sur de nombreux systèmes embarqués, il n'y a pas d'unité matérielle à virgule flottante. Ainsi, lorsque vous écrivez du code comme celui-ci:

float x = 1.0f, y = 2.0f;
return x + y;

Il se traduit par quelque chose comme ceci:

unsigned x = 0x3f800000, y = 0x40000000;
return _float_add(x, y);

Ensuite, le compilateur ou la bibliothèque standard doit fournir une implémentation de _float_add(), qui occupe de la mémoire sur votre système embarqué. Si vous comptez des octets sur un très petit système, cela peut s'additionner.

Un autre exemple courant est les entiers 64 bits (long long Dans la norme C depuis 1999), qui ne sont pas directement pris en charge par les systèmes 32 bits. Les anciens systèmes SPARC ne prenaient pas en charge la multiplication des nombres entiers, donc la multiplication devait être fournie par le runtime. Il existe d'autres exemples.

Autres langues

En comparaison, d'autres langages ont des primitives plus compliquées.

Par exemple, un symbole LISP nécessite beaucoup de support d'exécution, tout comme les tables dans Lua, les chaînes en Python, les tableaux dans Fortran, et cetera. Les types équivalents en C ne font généralement pas partie de la bibliothèque standard du tout (pas de symboles ni de tables standard) ou ils sont beaucoup plus simples et ne nécessitent pas beaucoup de support d'exécution (les tableaux en C ne sont essentiellement que des pointeurs, des chaînes terminées par nul sont presque aussi simple).

Structures de contrôle

Une structure de contrôle notable manquante dans C est la gestion des exceptions. La sortie non locale est limitée à setjmp() et longjmp(), qui ne font que sauvegarder et restaurer certaines parties de l'état du processeur. Par comparaison, le runtime C++ doit parcourir la pile et appeler les destructeurs et les gestionnaires d'exceptions.

143
Dietrich Epp

En fait, je parie que le contenu de cette introduction n'a pas beaucoup changé depuis 1978, lorsque Kernighan et Ritchie les ont écrits pour la première fois dans la première édition du livre, et ils se réfèrent à l'histoire et à l'évolution de C à cette époque plus que moderne. implémentations.

Les ordinateurs ne sont fondamentalement que des banques de mémoire et des processeurs centraux, et chaque processeur fonctionne à l'aide d'un code machine; une partie de la conception de chaque processeur est une architecture de jeu d'instructions, appelée Assembly Language , qui mappe un à un à partir d'un ensemble de mnémoniques lisibles par l'homme au code machine, qui est tous des nombres.

Les auteurs du langage C - et des langages B et BCPL qui l'ont immédiatement précédé - avaient l'intention de définir des constructions dans le langage qui étaient aussi efficacement compilées dans Assembly que possible ... en fait, ils ont été contraints par des limitations dans la cible Matériel. Comme d'autres réponses l'ont souligné, cela impliquait les branches (GOTO et autre contrôle de flux en C), les mouvements (affectation), les opérations logiques (& | ^), l'arithmétique de base (ajout, soustraction, incrémentation, décrémentation) et l'adressage de la mémoire (pointeurs ). Un bon exemple est les opérateurs pré/post-incrémentation et décrémentation en C, qui auraient été ajoutés au langage B par Ken Thompson spécifiquement parce qu'ils étaient capables de traduire directement en un seul opcode une fois compilés.

C'est ce que les auteurs ont voulu dire lorsqu'ils ont dit "pris en charge directement par la plupart des ordinateurs". Ils ne signifiaient pas que d'autres langages contenaient des types et des structures qui n'étaient pas directement pris en charge - ils signifiaient que par conception Les constructions C traduisent la plupart directement (parfois littéralement directement) dans Assembly.

Cette relation étroite avec l'assemblage sous-jacent, tout en fournissant tous les éléments requis pour une programmation structurée, est ce qui a conduit à l'adoption précoce de C, et ce qui en fait un langage populaire aujourd'hui dans des environnements où l'efficacité du code compilé est toujours la clé.

Pour une description intéressante de l'histoire du langage, voir Le développement du langage C - Dennis Ritchie

37
John Castleman

La réponse courte est que la plupart des constructions de langage prises en charge par C sont également prises en charge par le microprocesseur de l'ordinateur cible.

La réponse plus longue nécessite un peu de connaissance du langage d'assemblage. En C, une déclaration comme celle-ci:

int myInt = 10;

se traduirait par quelque chose comme ceci dans l'Assemblée:

myInt dw 1
mov myInt,10

Comparez cela à quelque chose comme C++:

MyClass myClass;
myClass.set_myInt(10);

Le code de langage d'assemblage résultant (en fonction de la taille de MyClass ()), pourrait ajouter jusqu'à des centaines de lignes de langage d'assemblage.

Sans réellement créer des programmes en langage assembleur, le C pur est probablement le code le plus "fin" et le plus "serré" dans lequel vous pouvez créer un programme.

MODIFIER

Compte tenu des commentaires sur ma réponse, j'ai décidé de faire un test, juste pour ma propre raison. J'ai créé un programme appelé "test.c", qui ressemblait à ceci:

#include <stdio.h>

void main()
{
    int myInt=10;

    printf("%d\n", myInt);
}

J'ai compilé ceci jusqu'à l'Assemblée en utilisant gcc. J'ai utilisé la ligne de commande suivante pour le compiler:

gcc -S -O2 test.c

Voici le langage d'assemblage résultant:

    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB24:
    .cfi_startproc
    movl    $10, %edx
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    jmp __printf_chk
    .cfi_endproc
.LFE24:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Je crée ensuite un fichier appelé "test.cpp" qui définit une classe et génère la même chose que "test.c":

#include <iostream>
using namespace std;

class MyClass {
    int myVar;
public:
    void set_myVar(int);
    int get_myVar(void);
};

void MyClass::set_myVar(int val)
{
    myVar = val;
}

int MyClass::get_myVar(void)
{
    return myVar;
}

int main()
{
    MyClass myClass;
    myClass.set_myVar(10);

    cout << myClass.get_myVar() << endl;

    return 0;
}

Je l'ai compilé de la même manière, en utilisant cette commande:

g++ -O2 -S test.cpp

Voici le fichier d'assemblage résultant:

    .file   "test.cpp"
    .section    .text.unlikely,"ax",@progbits
    .align 2
.LCOLDB0:
    .text
.LHOTB0:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9set_myVarEi
    .type   _ZN7MyClass9set_myVarEi, @function
_ZN7MyClass9set_myVarEi:
.LFB1047:
    .cfi_startproc
    movl    %esi, (%rdi)
    ret
    .cfi_endproc
.LFE1047:
    .size   _ZN7MyClass9set_myVarEi, .-_ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE0:
    .text
.LHOTE0:
    .section    .text.unlikely
    .align 2
.LCOLDB1:
    .text
.LHOTB1:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9get_myVarEv
    .type   _ZN7MyClass9get_myVarEv, @function
_ZN7MyClass9get_myVarEv:
.LFB1048:
    .cfi_startproc
    movl    (%rdi), %eax
    ret
    .cfi_endproc
.LFE1048:
    .size   _ZN7MyClass9get_myVarEv, .-_ZN7MyClass9get_myVarEv
    .section    .text.unlikely
.LCOLDE1:
    .text
.LHOTE1:
    .section    .text.unlikely
.LCOLDB2:
    .section    .text.startup,"ax",@progbits
.LHOTB2:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1049:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $10, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1049:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .text.unlikely
.LCOLDB3:
    .section    .text.startup
.LHOTB3:
    .p2align 4,,15
    .type   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, @function
_GLOBAL__sub_I__ZN7MyClass9set_myVarEi:
.LFB1056:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1056:
    .size   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, .-_GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Comme vous pouvez le voir clairement, le fichier Assembly résultant est beaucoup plus volumineux sur le fichier C++ que sur le fichier C. Même si vous supprimez toutes les autres choses et comparez simplement le C "principal" au C++ "principal", il y a beaucoup de choses supplémentaires.

14
Icemanind

K&R signifie que la plupart des expressions C (signification technique) correspondent à une ou à quelques instructions d'assemblage, et non à un appel de fonction à une bibliothèque de support. Les exceptions habituelles sont la division entière sur les architectures sans instruction div matérielle ou la virgule flottante sur les machines sans FPU.

Il y a une citation:

C combine la flexibilité et la puissance du langage d'assemblage avec la convivialité du langage d'assemblage.

( trouvé ici . Je pensais me souvenir d'une variation différente, comme "la vitesse du langage d'assemblage avec la commodité et l'expressivité du langage d'assemblage".)

long int est généralement de la même largeur que la machine native enregistre.

Certains langages de niveau supérieur définissent la largeur exacte de leurs types de données et les implémentations sur toutes les machines doivent fonctionner de la même manière. Pas C, cependant.

Si vous voulez travailler avec des bits 128 bits sur x86-64, ou dans le cas général BigInteger de taille arbitraire, vous avez besoin d'une bibliothèque de fonctions pour cela. Tous les processeurs utilisent désormais le complément 2s comme représentation binaire d'entiers négatifs, mais même ce n'était pas le cas lorsque C a été conçu. (C'est pourquoi certaines choses qui donneraient des résultats différents sur des machines sans complément 2s ne sont pas définies techniquement dans les normes C.)

Les pointeurs C vers des données ou des fonctions fonctionnent de la même manière que les adresses d'assembly.

Si vous voulez des références comptées, vous devez le faire vous-même. Si vous voulez des fonctions de membre virtuel c ++ qui appellent une fonction différente selon le type d'objet vers lequel pointe votre pointeur, le compilateur C++ doit générer bien plus qu'une simple instruction call avec une adresse fixe.

Les chaînes ne sont que des tableaux

En dehors des fonctions de bibliothèque, les seules opérations de chaîne fournies sont la lecture/écriture d'un caractère. Pas de concaténation, pas de sous-chaîne, pas de recherche. (Les chaînes sont stockées en tant que terminaison nulle ('\0') tableaux d'entiers 8 bits, pas de pointeur + longueur, donc pour obtenir une sous-chaîne, vous devrez écrire un nul dans la chaîne d'origine.)

Les processeurs ont parfois des instructions conçues pour être utilisées par une fonction de recherche de chaîne, mais traitent généralement un octet par instruction exécutée, en boucle. (ou avec le préfixe x86 rep. Peut-être que si C a été conçu sur x86, la recherche de chaîne ou la comparaison serait une opération native, plutôt qu'un appel de fonction de bibliothèque.)

De nombreuses autres réponses donnent des exemples de choses qui ne sont pas prises en charge nativement, comme la gestion des exceptions, les tables de hachage, les listes. La philosophie de conception de K&R est la raison pour laquelle C n'en a pas nativement.

7
Peter Cordes

Le langage d'assemblage d'un processus traite généralement des sauts (aller à), des instructions, des instructions de déplacement, de l'arthrite binaire (XOR, NAND, AND OR, etc.), des champs de mémoire (ou adresse). Catégorise la mémoire en deux types, instruction et données. C'est à peu près tout ce qu'est un langage d'assemblage (je suis sûr que les programmeurs d'assemblages diront qu'il y a plus que cela, mais cela se résume à cela en général). C ressemble beaucoup à cette simplicité.

C est d'assembler ce que l'algèbre est à l'arithmétique.

C résume les bases de l'assemblage (le langage du processeur). Est probablement une déclaration plus vraie que "Parce que les types de données et les structures de contrôle fournis par C sont pris en charge directement par la plupart des ordinateurs"

6
terary

Existe-t-il un exemple de type de données ou de structure de contrôle qui n'est pas directement pris en charge par un ordinateur?

Tous les types de données fondamentaux et leurs opérations en langage C peuvent être implémentés par une ou quelques instructions en langage machine sans boucle - ils sont directement pris en charge par (pratiquement tous) les CPU.

Plusieurs types de données populaires et leurs opérations nécessitent des dizaines d'instructions en langage machine, ou nécessitent l'itération d'une boucle d'exécution, ou les deux.

De nombreux langages ont une syntaxe abrégée spéciale pour ces types et leurs opérations - l'utilisation de ces types de données en C nécessite généralement de taper beaucoup plus de code.

Ces types de données et opérations incluent:

  • manipulation de chaînes de texte de longueur arbitraire - concaténation, sous-chaîne, attribution d'une nouvelle chaîne à une variable initialisée avec une autre chaîne, etc. ('s = "Hello World!"; s = (s + s) [2: -2] 'en Python)
  • ensembles
  • des objets avec des destructeurs virtuels imbriqués, comme en C++ et dans tout autre langage de programmation orienté objet
  • Multiplication et division matricielles 2D; résolution de systèmes linéaires ("C = B/A; x = A\b" dans MATLAB et de nombreux langages de programmation de tableaux)
  • expressions régulières
  • tableaux de longueur variable - en particulier, ajouter un élément à la fin du tableau, ce qui (parfois) nécessite d'allouer plus de mémoire.
  • lire la valeur des variables qui changent de type à l'exécution - parfois c'est un flottant, d'autres fois c'est une chaîne
  • tableaux associatifs (souvent appelés "cartes" ou "dictionnaires")
  • listes
  • ratios ("(+ 1/3 2/7)" donne "13/21" dans LISP )
  • arithmétique à précision arbitraire (souvent appelée "bignums")
  • convertir les données en une représentation imprimable (la méthode ".tostring" en JavaScript)
  • saturation des nombres à virgule fixe (souvent utilisés dans les programmes C intégrés)
  • évaluer une chaîne tapée au moment de l'exécution comme s'il s'agissait d'une expression ("eval ()" dans de nombreux langages de programmation).

Toutes ces opérations nécessitent des dizaines d'instructions en langage machine ou nécessitent l'itération d'une boucle d'exécution sur presque tous les processeurs.

Certaines structures de contrôle populaires qui nécessitent également des dizaines d'instructions en langage machine ou de bouclage comprennent:

  • fermetures
  • suites
  • exceptions
  • évaluation paresseuse

Qu'il soit écrit en C ou dans un autre langage, lorsqu'un programme manipule de tels types de données, l'UC doit finalement exécuter toutes les instructions nécessaires pour manipuler ces types de données. Ces instructions sont souvent contenues dans une "bibliothèque". Chaque langage de programmation, même C, a une "bibliothèque d'exécution" pour chaque plate-forme qui est incluse par défaut dans chaque exécutable.

La plupart des personnes qui écrivent des compilateurs mettent les instructions pour manipuler tous les types de données qui sont "intégrés dans le langage" dans leur bibliothèque d'exécution. Parce que C n'a aucun des types de données et des opérations et structures de contrôle ci-dessus intégrés dans le langage, aucun d'entre eux n'est inclus dans l'exécution C - bibliothèque de temps - qui rend la bibliothèque d'exécution C plus petite que la bibliothèque d'exécution d'autres langages de programmation qui ont plus de choses ci-dessus intégrées au langage.

Lorsqu'un programmeur souhaite qu'un programme - en C ou dans tout autre langage de son choix - manipule d'autres types de données qui ne sont pas "intégrés au langage", ce programmeur demande généralement au compilateur d'inclure des bibliothèques supplémentaires avec ce programme, ou parfois (pour "éviter les dépendances") écrit une autre implémentation de ces opérations directement dans le programme.

5
David Cary

Méfiez-vous des comparaisons trompeuses

  1. L'instruction s'appuie sur la notion de "bibliothèque d'exécution", qui est pour la plupart passée de mode depuis, au moins pour les langages de haut niveau traditionnels. (Il est toujours pertinent pour les plus petits systèmes embarqués.) L'exécution est le support minimal qu'un programme dans ce langage nécessite pour s'exécuter lorsque vous utilisez uniquement des constructions intégrées dans le langage (par opposition à l'appel explicite d'une fonction fournie par une bibliothèque) .
  2. En revanche, les langages modernes ont tendance à ne pas faire la distinction entre l'exécution et la bibliothèque standard, cette dernière étant souvent assez étendue.
  3. Au moment du livre K&R, C n'avait même pas de bibliothèque standard . Au contraire, les bibliothèques C disponibles différaient beaucoup entre les différentes saveurs d'Unix.
  4. Pour comprendre la déclaration, vous ne devez pas comparer avec les langues avec une bibliothèque standard (comme Lua et Python mentionné dans d'autres réponses), mais avec les langues avec plus intégré (comme LISP ancien et FORTRAN ancien mentionné dans d'autres réponses). D'autres exemples seraient BASIC (interactif, comme LISP) ou Pascal (compilé, comme FORTRAN) qui ont tous deux (entre autres) des fonctionnalités d'entrée/sortie intégré dans la langue elle-même.
  5. En revanche, il n'existe aucun moyen standard d'obtenir les résultats de calcul à partir d'un programme C qui utilise uniquement l'exécution, pas une bibliothèque.
5
Lutz Prechelt

Quels sont les types de données intégrés dans C? Ce sont des choses comme int, char, * int, float, tableaux etc ... Ces types de données sont compris par le CPU. Le CPU sait comment travailler avec des tableaux, comment déréférencer des pointeurs et comment effectuer des calculs arithmétiques sur des pointeurs, des entiers et des nombres à virgule flottante.

Mais lorsque vous passez à des langages de programmation de niveau supérieur, vous avez intégré des types de données abstraits et des constructions plus complexes. Par exemple, regardez la vaste gamme de classes intégrées dans le langage de programmation C++. Le CPU ne comprend pas les classes, les objets ou les types de données abstraits, donc le runtime C++ comble le fossé entre le CPU et le langage. Ce sont des exemples de types de données qui ne sont pas directement pris en charge par la plupart des ordinateurs.

4
hhafez

Cela dépend de l'ordinateur. Sur le PDP-11, où C a été inventé, long était mal pris en charge (il y avait un module complémentaire optionnel que vous pouviez acheter qui supportait certaines opérations 32 bits, mais pas toutes). Il en va de même à divers degrés sur tout système 16 bits, y compris le PC IBM d'origine. Et de même pour les opérations 64 bits sur des machines 32 bits ou dans des programmes 32 bits, bien que le langage C à l'époque du livre K&R ne comportait aucune opération 64 bits. Et bien sûr, il y a eu de nombreux systèmes dans les années 80 et 90 [y compris les processeurs 386 et 486], et même certains systèmes embarqués aujourd'hui, qui ne prenaient pas directement en charge l'arithmétique à virgule flottante (float ou double).

Pour un exemple plus exotique, certaines architectures informatiques prennent uniquement en charge les pointeurs "orientés Word" (pointant vers un entier de deux octets ou de quatre octets en mémoire) et les pointeurs d'octets (char * ou void *) a dû être implémenté en ajoutant un champ de décalage supplémentaire. Cette question donne quelques détails sur ces systèmes.

Les fonctions "bibliothèque d'exécution" auxquelles il se réfère ne sont pas celles que vous verrez dans le manuel, mais des fonctions comme celles-ci, dans la bibliothèque d'exécution d'un compilateur moderne , qui sont utilisées pour implémenter les opérations de type de base qui ne sont pas pris en charge par la machine. La bibliothèque d'exécution à laquelle K&R faisait référence se trouve sur Le site Web de la Unix Heritage Society - vous pouvez voir des fonctions comme ldiv (distincte de la fonction C du même nom, qui n'existe pas à l'époque) qui est utilisé pour implémenter la division des valeurs 32 bits, que le PDP-11 ne supportait même pas avec le module complémentaire, et csv (et cret également dans csv.c) qui sauvegarde et restaure les registres sur la pile pour gérer les appels et les retours de fonctions.

Ils faisaient probablement référence à leur choix de ne pas prendre en charge de nombreux types de données qui ne sont pas directement pris en charge par la machine sous-jacente, contrairement à d'autres langages contemporains tels que FORTRAN, qui avaient une sémantique de tableau qui ne correspondait pas aussi bien à la prise en charge du pointeur sous-jacent du processeur que Tableaux de C. Le fait que les tableaux C soient toujours indexés zéro et toujours de taille connue dans tous les rangs, mais le premier signifie qu'il n'est pas nécessaire de stocker les plages d'index ou les tailles des tableaux, et pas besoin de fonctions de bibliothèque d'exécution pour y accéder - le compilateur peut simplement coder en dur l'arithmétique du pointeur nécessaire.

4
Random832

L'instruction signifie simplement que les structures de données et de contrôle en C sont orientées machine.

Il y a deux aspects à considérer ici. La première est que le langage C a une définition (norme ISO) qui permet une latitude dans la définition des types de données. Cela signifie que les implémentations du langage C sont adaptées à la machine. Les types de données d'un compilateur C correspondent à ce qui est disponible dans la machine que le compilateur cible, car le langage a la latitude pour cela. Si une machine a une taille de mot inhabituelle, comme 36 bits, alors le type int ou long peut être fait pour se conformer à cela. Les programmes qui supposent que int est exactement 32 bits s'arrêteront.

Deuxièmement, en raison de ces problèmes de portabilité, il y a un deuxième effet. D'une certaine manière, l'énoncé dans le K&R est devenu une sorte de prophétie auto-réalisatrice, ou peut-être à l'envers. C'est-à-dire que les implémenteurs de nouveaux processeurs sont conscients du besoin pressant de prendre en charge les compilateurs C, et ils savent qu'il existe beaucoup de code C qui suppose que "chaque processeur ressemble à un 80386". Les architectures sont conçues en pensant au C: et pas seulement au C, mais aussi aux fausses idées courantes sur la portabilité du C. Vous ne pouvez tout simplement plus introduire une machine avec des octets de 9 bits ou quoi que ce soit à usage général. Les programmes qui supposent que le type char fait exactement 8 bits de large seront interrompus. Seuls certains programmes écrits par des experts en portabilité continueront de fonctionner: probablement pas assez pour rassembler un système complet avec une chaîne d'outils, un noyau, un espace utilisateur et des applications utiles, avec un effort raisonnable. En d'autres termes, les types C ressemblent à ce qui est disponible à partir du matériel parce que le matériel a été conçu pour ressembler à un autre matériel pour lequel de nombreux programmes C non portables ont été écrits.

Existe-t-il un exemple de type de données ou de structure de contrôle qui n'est pas directement pris en charge par un ordinateur?

Types de données non directement pris en charge dans de nombreux langages machine: entier multi-précision; liste chaînée; table de hachage; chaîne de caractères.

Structures de contrôle non directement prises en charge dans la plupart des langages machine: continuation de première classe; coroutine/fil; Générateur; gestion des exceptions.

Tous ces éléments nécessitent un code de support à l'exécution considérable créé à l'aide de nombreuses instructions générales et de types de données plus élémentaires.

C a certains types de données standard qui ne sont pas pris en charge par certaines machines. Depuis C99, C a des nombres complexes. Ils sont constitués de deux valeurs à virgule flottante et conçus pour fonctionner avec des routines de bibliothèque. Certaines machines n'ont aucune unité à virgule flottante.

En ce qui concerne certains types de données, ce n'est pas clair. Si une machine prend en charge l'adressage de la mémoire en utilisant un registre comme adresse de base et un autre comme déplacement à l'échelle, cela signifie-t-il que les tableaux sont un type de données directement pris en charge?

Par ailleurs, en parlant de virgule flottante, il existe une standardisation: IEEE 754 virgule flottante. La raison pour laquelle votre compilateur C possède un double qui convient au format à virgule flottante pris en charge par le processeur n'est pas seulement parce que les deux ont été conçus pour être d'accord, mais parce qu'il existe une norme indépendante pour cette représentation.

3
Kaz

Pris en charge directement doit être compris comme mappant efficacement le jeu d'instructions du processeur.

  • La prise en charge directe des types entiers est la règle, sauf pour les tailles longues (peuvent nécessiter des routines arithmétiques étendues) et courtes (peuvent nécessiter un masquage).

  • La prise en charge directe des types à virgule flottante nécessite la disponibilité d'un FPU.

  • La prise en charge directe des champs de bits est exceptionnelle.

  • Les structures et les tableaux nécessitent un calcul d'adresse, directement pris en charge dans une certaine mesure.

  • Les pointeurs sont toujours directement pris en charge via l'adressage indirect.

  • goto/if/while/for/do sont directement pris en charge par les branches inconditionnelles/conditionnelles.

  • le commutateur peut être directement pris en charge lorsqu'une table de saut s'applique.

  • Les appels de fonction sont directement pris en charge au moyen des fonctionnalités de pile.

2
Yves Daoust

Des choses comme

  • Listes Utilisé dans presque tous les langages fonctionnels.

  • Exceptions.

  • Tableaux associatifs (Cartes) - inclus dans par ex. PHP et Perl.

  • Collecte des ordures.

  • Types de données/structures de contrôle inclus dans de nombreuses langues, mais non directement pris en charge par le CPU.

2
MTilsted