int impliciteC a trois ancêtres : CPL, BCPL et B.
CPL (pour Combined Programming Language) a été conçu au début des années 60 par les universités de Cambridge et de Londres. C'était un grand projet académique consistant à créer un langage englobant beaucoup de concepts. CPL devait aussi être fortement typé, c'est-à-dire que les données n'étaient pas traitées comme des tas de bits (qu'elles sont), mais comme de vrais nombres entiers, réels, booléens, caractères, tableaux, listes... qu'on ne peut pas mélanger ; comme les pommes et les oranges chez les professeurs de physique.
Aujourd'hui, le typage fort est une des caractèristiques du langage Ada, qui est souvent opposé à C. Mais CPL était trop complexe pour l'époque et il semble que jamais personne n'ait réussi à terminer l'écriture d'un compilateur. En tout cas ce langage a disparu sans laisser de trace quelque-part dans les années 70.
Réf. : LinuxGuruz, Wikipedia
BCPL (pour Basic CPL) a été conçu à Cambridge en 1966 par Martin Richards. L'année suivante il alla au MIT et écrivit un premier compilateur. BCPL est une version fortement simplifiée de CPL avec notamment un seul type de donnée, le mot machine, c'est-à-dire le nombre typique de bits qu'un ordinateur traite en une instruction machine (addition, soustraction, multiplication, copie...) La notion est devenue un peu floue avec les processeurs actuels qui peuvent traiter des données de toutes sortes de tailles, mais on peut encore dire que les Pentium et PowerPC sont 32 bits tandis que les Alpha, POWER et Itanium sont 64 bits. À l'époque c'était plus varié et ce n'était pas forcément des puissances de 2 multiples de 8 ; par exemple 40 bits, 36 bits, 18 bits ou 16 bits.
BCPL s'accomode de toutes ces tailles de mot machine. Il est donc à la fois portable et proche du matériel, donc efficace pour la programmation système. BCPL a servi à écrire divers systèmes d'exploitation de l'époque, dont un (TripOS) qui s'est retrouvé partiellement porté sur l'Amiga pour devenir la partie AmigaDOS du système.
Aujourd'hui cependant, je ne suis pas sûr que BCPL soit encore utilisé par quelqu'un d'autre que son inventeur. En tout cas je ne connais aucun programme actuel écrit en BCPL. C'est peut-être dû au fait que C a repris et étendu les qualités de BCPL.
Réf. :
Manuel BCPL,
exemple de
http://www.lysator.liu.se/c/clive-on-history.html :
MANIFEST ${ TOPFACT = 10 $} // Equivalent of "const int TOPFACT = 10"
LET infact (n) = VALOF
$(
LET f, j = 1., 0.
FOR i = 0 TO n // Declares i for the next block only
$(
f #*:= j; // := is assign, = is compare
fact!i := f; // assignment doesn't return a value
j #+:= 1.
$)
RESULTIS f
$)
AND fact = VEC TOPFACT; // As in B, allocates 0 to TOPFACT
B a été créé par Ken Thompson vers 1970 dans les laboratoires Bell d'AT&T. L'année précédente il avait écrit en assembleur la première version de UNIX sur un PDP-7 contenant 8 kilo-mots de 18 bits. Après il voulut proposer un langage sur UNIX. Il semble qu'il ait d'abord pensé à porter FORTRAN, mais que très vite (en une semaine) il conçut son propre langage : B.
B est une simplification de BCPL (3 caractères de moins), un peu forcée par les limitations du PDP-7. Mais la syntaxe très sobre de B toute en lettres minuscules correspond surtout aux goûts de Thompson. C a repris la syntaxe de B avec un minimum de changements.
B a été porté et utilisé sur quelques autres systèmes. Mais cette même année 1970, le succès du projet UNIX justifia l'achat d'un PDP-11. Celui-ci avait des mots machine de 16 bits mais il était aussi capable de traiter des octets (24 Ko de RAM en tout) dans chacun duquel pouvait être stocké un caractère. B ne traitait que des mots machines, donc le passage de 18 à 16 bits n'était pas problématique, mais il était impossible de traiter efficacement les caractères de 8 bits. Pour bien exploiter les capacités de la machine, B a donc commencé à être étendu en ajoutant un type pour les caractères...
Réf. :
Thompson's B Manual,
exemple de http://www.lysator.liu.se/c/clive-on-history.html :
infact (n)
{
auto f, i, j; /* no initialization for auto variables */
extrn fact; /* "What would I do differently if designing
* UNIX today? I'd spell creat() with an e."
* -- Ken Thompson, approx. wording */
f = 1.; /* floating point constant */
j = 0.;
for (i = 0; i <= n; ++i) {
fact[i] = f =#* j; /* note spelling =#* not #*= */
j =#+ 1.; /* #+ for floating add */
}
return (f); /* at least, I think the () were required */
}
TOPFACT = 10; /* equivalent of #define, allows numeric values only */
fact[TOPFACT];
C a été développé par un collègue de Ken Thompson, Dennis Ritchie qui pensait d'abord faire uniquement un New B ou NB. Mais Ritchie ajouta plus que les caractères : par exemple les tableaux, les pointeurs ou les nombres à virgule flottante. 1972 fût l'année de développement la plus productive et sans doute l'année de baptême de C. En 1973 C fut suffisamment au point pour que 90% de UNIX puisse être récrit avec. En 1974 les laboratoires Bell ont accordés des licences UNIX aux universités et c'est ainsi que C a commencé à être distribué.
Réf. : The Development of the C Language,
Very early C compilers and language,
exemple de http://www.lysator.liu.se/c/clive-on-history.html :
float infact (n) int n;
{
float f = 1;
int i;
extern float fact[];
for (i = 0; i <= n; ++i)
fact[i] = f *= i;
return d;
}
#define TOPFACT 10
float fact[TOPFACT+1];
Ken Thompson (assis) et Dennis Ritchie devant un PDP-11 fonctionnant avec
UNIX vers 1972 :
Une fois rendu public, le langage C a peu changé. D'un autre côté, C++ est une gigantesque extension de C ; ceci explique peut-être cela. D'autres langages comme Objective C, Java, JavaScript ou C# ont largement repris la syntaxe de C, mais sans être compatibles.
#include <stdio.h>
main(argc,
argv)
int argc;
char ** argv;
{
printf("hello, world\n");
}
La première évolution de C qui nous concerne directement date de 1978 lorsque Brian Kernighan et Dennis Ritchie ont publié la première édition du livre : The C Programming Language. Il décrit ce qu'on appelle actuellement le K&R C, C traditionnel, voire vieux C. Peu après sa publication, de très nombreux compilateurs C ont commencé à apparaître. Les programmes portables écrits dans les années 80 sont donc en K&R C. En fait même les programmes récents qui doivent être très portables, comme GCC 3, sont encore compilables en K&R C. Des systèmes commerciaux comme HP/UX n'ont parfois qu'un compilateur K&R C en standard.
#include <iostream.h>
int main(int argc,
char * argv[])
{
cout << "hello, world"
<< endl;
return 0;
}
À partir de 1980
Bjarne Stroustrup
a étendu C avec le concept de classe. Ce langage étendu a d'abord été appelé
C with Classes puis C++ en 1983. C++ a énormément évolué,
mais en restant le plus compatible possible avec C.
En fait on tombe même parfois sur des programmes C++ qui utilisent
tellement peu les possibilités du langage qu'on ne voit pas bien la
différence avec un programme C (telnet sur Debian).
#include <stdio.h>
main(int argc,
char * argv[])
{
printf("hello, world\n");
return 0;
}
Un comité de standardisation a été créé en 1983 pour éviter que les quelques ambiguïtés et insuffisances du K&R C ne conduisent à des divergences importantes entre les compilateurs. C'est ce qui a donné en 1989 le standard appelé ANSI C. Il a repris quelques bonnes idées de C++ tout en restant compatible avec K&R C.
Le degré de compatibilité atteint est suffisant pour que Kernighan et Ritchie n'aient eu qu'à adapter légèrement la seconde édition du The C Programming Language pour qu'il décrive ANSI C. En plus selon Stroustrup tous les exemples de cette seconde édition sont aussi des programmes C++.
Depuis les années 90, ANSI C est l'évolution la plus utilisée de C.
#include <iostream>
int main(int argc,
char * argv[])
{
std::cout << "hello, world"
<< std::endl;
}
C++ a évolué très longtemps. Ce n'est qu'en 1998, 8 ans après la création d'un comité, que le standard ISO C++ a été officiellement publié. Mais ce standard est tellement complexe (et défectueux) qu'en 2002 je n'en connais pas encore de mise en œuvre vraiment complète et correcte. La compatibilité avec C reste très bonne. Les syntaxes obsolètes et problématiques de K&R C ont cependant été abandonnées.
#include <stdio.h>
int main(int argc,
char * argv[])
{
printf("hello, world\n");
}
Le dernier né de l'histoire est C99 (standard ISO de 1999) qui est une petite évolution de l'ANSI C de 1989. Les évolutions ne sont pas compatibles avec C++ et n'ont pas attiré beaucoup d'intérêt.
GCC supporte assez bien C99. Or comme le noyau Linux utilise assez à fond les possibilités de GCC, on peut considérer que les versions récentes de Linux sont écrites en C99, plus des extensions propres à GCC. Côté compatibilité, le support des syntaxes obsolètes de K&R C a été abandonné.
On tombe encore facilement sur du K&R C. Par exemple le programme C le plus court qu'on rencontre dans la nature est :
main(){}
Ce programme est en fait un programme K&R C typique car :
Il n'indique pas explicitement le type int retourné par
main. Cette pratique est encore supportée par
ANSI C, mais plus par C++.
Il n'utilise pas void pour indiquer que main
n'a pas de paramètre. En effet, void n'existe qu'à partir de
ANSI C et C++.
Il est toutefois intéressant de noter que pour C++,
int main() indique effectivement que main ne reçoit
aucun paramètre et que la notation (void) est devenue inusitée.
Il manque une instruction return. Cette mauvaise pratique
vient principalement de l'exemple hello, world de Kernighan et
Ritchie.
À noter que Standard C++ a fini par résoudre
cette pratique en stipulant qu'un return 0; est implicitement
ajouté à une définition de main qui n'en contient pas.
Le programme int main(){} est donc parfaitement propre en
Standard C++ (c'est true).
int impliciteLa pratique de ne pas écrire explicitement le type int est
en fait courante en K&R C :
/* Exemple en vieux C */
*g;
main() {
auto i; g=&i; *g=0; return i;
}
/* Exemple en C moderne */
int *g;
int main(void) {
int i; g=&i; *g=0; return i;
}
Il faut noter qu'en B on n'indiquait pas non plus le type
puisqu'il n'y avait que le mot machine, qui est devenu int en C...
La différence la plus importante entre ANSI C et K&R C se situe dans les déclarations et définitions de fonctions :
f(); /* déclaration de f, returne un int (implicite),
paramètres non spécifiés */
main() { return f(2,0); }
int f(a, b) /* définition de f */
int a, b; /* avec le type des paramètres donné après */
{
return a * b;
}
Seul le nom des paramètres était donné dans la liste des paramètres. Leur type était déclaré en-dessous, avant l'accolade ouvrante.
De ce fait, les prototypes n'existaient pas en K&R C. On pouvait déclarer une fonction, mais cela ne précisait pas le type de ses paramètres.
Comme le type des paramètres n'est pas connu au moment de l'appel,
il y a promotion de type lors du passage du paramètre, comme avant une
opération. Les char et les short sont notamment
promus en int et les float en double.
On trouve encore les mêmes promotions sur les paramètres de
printf correspondant aux ... de
int printf(const char*,...);. Ce qui explique qu'on puisse
écrire short s; printf("%d", s); alors que %d
attend un int.
© 2002 Marc Mongenet, pour le Groupe des Utilisateurs Linux du Léman (GULL).