Quelles sont les principales différences entre ANSI C et K & R C?

L’ article de Wikipedia sur ANSI C dit:

L’un des objectives du processus de normalisation ANSI C était de produire un sur-ensemble de K & R C (le premier standard publié), incorporant bon nombre des fonctionnalités non officielles introduites par la suite. Cependant, le comité de normalisation a également inclus plusieurs nouvelles fonctionnalités, telles que des prototypes de fonctions (empruntés au langage de programmation C ++) et un préprocesseur plus performant. La syntaxe des déclarations de parameters a également été modifiée pour refléter le style C ++.

Cela me fait penser qu’il y a des différences. Cependant, je n’ai pas vu de comparaison entre K & R C et ANSI C. Existe-t-il un tel document? Si non, quelles sont les principales différences?

EDIT: Je crois que le livre de K & R indique “ANSI C” sur la couverture. Au moins je crois que la version que j’ai à la maison fait. Alors peut-être qu’il n’y a plus de différence?

Il peut y avoir une certaine confusion ici sur ce que “K & R C” est. Le terme fait référence au langage décrit dans la première édition de “The C Programming Language”. Grosso modo: le langage d’entrée du compilateur C de Bell Labs vers 1978.

Kernighan et Ritchie ont participé au processus de normalisation ANSI. Le dialecte “ANSI C” remplacé “K & R C” et les éditions suivantes du “Langage de programmation C” adoptent les conventions ANSI. “K & R C” est une “langue morte”, sauf dans la mesure où certains compilateurs acceptent toujours le code hérité.

Les prototypes fonctionnels constituaient le changement le plus évident entre K & R C et C89, mais il y en avait beaucoup d’autres. La normalisation de la bibliothèque C a également nécessité beaucoup de travail. Même si la bibliothèque C standard était une codification des pratiques existantes, elle codifiait de nombreuses pratiques existantes, ce qui rendait la tâche plus difficile. Le livre de PJ Plauger, The Standard C Library , est une excellente référence. Il explique également en arrière-plan les raisons pour lesquelles la bibliothèque a fini comme ça.

La norme ANSI / ISO C est très similaire à K & R C à bien des égards. Il était prévu que la plupart du code C existant repose sur des compilateurs ANSI sans beaucoup de modifications. De manière cruciale, cependant, à l’époque des normes, la sémantique du langage était sujette à interprétation par chaque fournisseur de compilateur. ANSI C a introduit une description commune de la sémantique du langage qui met tous les compilateurs sur un pied d’égalité. Il est facile de prendre cela pour acquis maintenant, environ 20 ans plus tard, mais il s’agissait là d’une réalisation importante.

Pour la plupart, si vous n’avez pas de base de code C pré-standard à maintenir, vous devriez être heureux de ne pas avoir à vous en préoccuper. Si vous le faites – ou pire encore, si vous essayez de mettre un ancien programme à niveau avec des normes plus modernes – vous avez alors mes sympathies.

Il y a quelques différences mineures, mais je pense que les éditions ultérieures de K & R sont destinées à ANSI C, donc il n’y a plus vraiment de différence.
“C Classic”, faute de meilleurs termes, avait une manière légèrement différente de définir les fonctions, à savoir

int f( p, q, r ) int p, float q, double r; { // Code goes here } 

Je crois que l’autre différence était les prototypes de fonctions. Les prototypes n’étaient pas obligés – en fait ils ne pouvaient pas – prendre une liste d’arguments ou de types. En ANSI C, ils le font.

  1. prototype de fonction.
  2. qualificatifs constants et volatiles.
  3. support de caractère large et internationalisation.
  4. permet au pointeur de fonction d’être utilisé sans déréférencement.

Une autre différence est que les types de retour de fonction et les types de paramètre n’ont pas besoin d’être définis. Ils seraient supposés être ints.

 f(x) { return x + 1; } 

et

 int f(x) int x; { return x + 1; } 

sont identiques.

  • PROTOTYPAGE DE FONCTION: le C ANSI adopte la technique du prototype de fonction c ++ où la définition et la déclaration de la fonction incluent les noms de fonction, les arguments t, les types de données et les types de données à valeur renvoyée. Le prototype de fonction permet aux compilateurs ANSI de rechercher les appels de fonction dans le programme utilisateur transmettant un numéro d’argument invalide. ou des types de données d’argument incompatibles. Ils corrigent une faiblesse majeure des compilateurs K & R C: un appel non valide dans un programme utilisateur passe souvent la compilation, mais provoque le blocage du programme lors de son exécution.

La différence est:

  1. Prototype
  2. support de caractère large et internationalisation
  3. Prise en charge des mots clés const et volatile
  4. autorise les pointeurs de fonction à être utilisés comme déréférencement

Les principales différences entre ANSI C et K & R C sont les suivantes:

  • prototypage de fonctions
  • support des qualificatifs const et volatile
  • soutenir les caractères larges et l’internationalisation
  • permettre aux pointeurs de fonction d’être utilisés sans déréférencement

ANSI C adopte la technique du prototype de fonction c ++ dans laquelle la définition et la déclaration de fonction incluent les noms de fonction, les types de données des arguments et les types de données de valeur de retour. Le prototype de fonction permet au compilateur ANSI C de vérifier les appels de fonction dans les programmes utilisateur qui transmettent un nombre d’arguments non valide ou des types de données d’arguments incompatibles. Ceux-ci corrigent une faiblesse majeure du compilateur K & R C.

Exemple: déclarer une fonction foo et obliger celui-ci à prendre deux arguments

  unsigned long foo (char* fmt, double data) { /*body of foo */ } 

Je pense que la plus grande différence concerne le prototypage de fonction et la syntaxe permettant de décrire les types d’arguments de fonction.

Une différence majeure dont personne n’a encore parlé est qu’avant ANSI, C était défini en grande partie par le précédent plutôt que par la spécification; dans les cas où certaines opérations auraient des conséquences prévisibles sur certaines plates-formes mais pas d’autres (par exemple, l’utilisation d’opérateurs relationnels sur deux pointeurs indépendants), précédents ont fortement recommandé de mettre les garanties de plate-forme à la disposition du programmeur. Par exemple:

  1. Sur les plates-formes qui définissent un classement naturel parmi tous les pointeurs vers tous les objects, l’application des opérateurs relationnels à des pointeurs arbitraires peut être utilisée pour obtenir ce classement.

  2. Sur les plateformes où le moyen naturel de vérifier si un pointeur est “supérieur à” un autre n’a jamais d’effet secondaire autre que de donner une valeur vraie ou fausse, l’application des opérateurs relationnels à des pointeurs arbitraires pourrait également être invoquée pour ne jamais avoir aucun côté. -effets autres que donner une valeur vraie ou fausse.

  3. Sur les plates-formes où deux types d’entiers ou plus partagent la même taille et la même représentation, un pointeur sur l’un de ces types d’entiers pourrait être utilisé pour lire ou écrire des informations d’un autre type avec la même représentation.

  4. Sur les plates-formes du complément à deux où les dépassements d’entier enveloppent naturellement en silence, une opération impliquant une valeur non signée inférieure à “int” peut être considérée comme se comportant comme si la valeur était non signée dans les cas où le résultat serait compris entre INT_MAX + 1u et UINT_MAX et qui n’a pas été promu à un type plus grand, ni utilisé comme opérande gauche de >> , ni opérande de / , % , ni opérateur de comparaison. Incidemment, la raison d’être de la norme en fait l’une des raisons pour lesquelles les petits types non signés encouragent la signature .

Avant la C89, on ne savait pas exactement quelle longueur les compilateurs de plates-formes où les hypothèses ci-dessus ne seraient pas fondées pourraient soutenir, de toute façon, ces hypothèses, mais il ne faisait guère de doute que les compilateurs de plates-formes pouvant facilement et à moindre coût défendre ces hypothèses devrait le faire. Les auteurs de la norme C89 n’ont pas pris la peine de le dire expressément pour les raisons suivantes:

  1. Les compilateurs dont les auteurs n’étaient pas délibérément obtus continueraient à faire de telles choses lorsque cela était pratique sans avoir à le savoir (la justification donnée pour promouvoir de petites valeurs non signées à la signature renforce fortement ce sharepoint vue).

  2. La norme exigeait seulement que les implémentations soient capables d’exécuter un programme éventuellement conçu sans débordement de stack, et reconnaissait qu’une implémentation obtuse pouvait traiter tout autre programme comme invoquant le comportement indéfini, mais ne pensait pas qu’il valait la peine de s’inquiéter des rédacteurs obtus du compilateur. des implémentations “conformes” mais inutiles.

Bien que “C89” ait été interprété à la fois comme signifiant “le langage défini par C89, plus les fonctionnalités supplémentaires et les garanties offertes par la plate-forme”, les auteurs de gcc ont préconisé une interprétation excluant toute fonctionnalité et garantie autres que celles prescrites par C89.

En dépit de toutes les revendications du contary, K & R était et est tout à fait capable de fournir tout type de matériel de bas en haut, proche du matériel. Le problème est maintenant de trouver un compilateur (de préférence gratuit) capable d’effectuer une compilation propre sur quelques millions de lignes de K & R C sans avoir à s’en occuper. Et d’exécuter sur quelque chose comme un processeur multicœur AMD.

Pour autant que je sache, après avoir examiné la source de la série GCC 4.xx, il n’existe pas de moyen simple de réactiver la fonctionnalité de décalage traditionnelle -crad-traditionnelle et -cpp-à son état de fonctionnement précédent, sans plus de précision que ce à quoi je suis habitué auparavant. et plus simple de construire un compilateur pré-ansi K & R à partir de zéro.