C=gestion de la mémoire
● pas de garbage collector interne au langage
● on peut utiliser celui de Boehm, Demers & Weiser
● mais: risque si l’on se mélange les pinceaux avec les malloc/free normaux, car ce n’est qu’une couche supplémentaire
C=langage compilé
● un programme C existe en 2 versions:
– les sources
– le binaire exécutable
● si on n’a que le binaire, le programme mourra
● problème inexistant pour les langages interprétés (Perl, Python, …)
C=typage statique
● déclaration des variables avec leurs types
● besoin de conversions explicites:
6 ⇒ « 6 » sprintf(tmp, »%d »,n);
« 6 » ⇒ 6 sscanf(tmp, »%d »,&n);
● typage dynamique: variables de bash
– la conversion est faite automatiquement quand c’est nécessaire
● idée similaire: autoboxing/unboxing en Java:
● si on a Integer foo(Integer i); on peut écrire: int i=foo(45);
● attention: toujours du typage statique, car effectué à la compilation, pas à l’exécution
C=typage faible
● on doit toujours préciser les types utilisés
● typage fort: le type est déterminé automatiquement en fonction de l’utilisation
● exemple: inférence de types en Caml
● somme d’une liste d’entiers:
# let rec sum l = match l with []->0 | x::y -> x+sum(y);; val sum : int list -> int = <fun>
● on n’a pas précisé qu’il s’agissait d’entiers
● Caml l’a déduit du +
● somme d’une liste de réels: # let rec sum l = match l with [] -> 0. | x::y -> x+.sum(y);;
val sum : float list -> float = <fun>
● taille d’une liste:
# let rec size l = match l with []->0
| x::y -> 1+size(y);; val size : ‘a list -> int = <fun> n’importe quel type convient dans ce cas
C=opérateurs figés
● pas de surcharge, impossible d’écrire a+b pour des nombres complexes
● surcharge d’opérateur possible en C++:
class Complex {
public:
float re,im;
Complex() {re=0; im=0;}
Complex(float a,float b) {re=a; im=b;}
friend Complex operator+(const Complex &a,const Complex &b) { Complex c;
c.re=a.re+b.re;
c.im=a.im+b.im;
return c;
}
};
● utilisation:
int main(int argc,char* argv[]) {
Complex a=Complex(4,5);
Complex b=Complex(0,7);
Complex c=a+b;
printf(« c=%g+%g*i\n »,c.re,c.im);
return 0;
}
$>g++ surcharge.cpp
$>./a.out
c=4+12*i
C=pas d’exceptions
● gestion des erreurs manuelles
● propager une erreur est lourd:
int fprintf_utf8(FILE* f,unichar* s) { while (*s) {
if (!fputc_utf8(*s,f)) return 0;
s++;
}
return 1;
}
int save_HTML(FILE* f,unichar* s) {
if (!fprintf_utf8(f,begin_tag)) return 0; if (!fprintf_utf8(f,s)) return 0;
if (!fprintf_utf8(f,end_tag)) return 0; return 1;
}
● solution: les exceptions (C++, Java, …)
void fprintf_utf8(FILE* f,unichar* s) {
while (*s) {
if (!fputc_utf8(*s,f)) throw IOException;
} s++;
/* Le throw pourrait même être dans */
} /* la fonction fputc_utf8 */
int save_HTML(FILE* f,unichar* s) {
try {
fprintf_utf8(f,begin_tag);
fprintf_utf8(f,s);
fprintf_utf8(f,end_tag);
return 1;
} catch (MyException exception) { return 0;
}
}
C=noms globaux
● pas d’espaces de nommage paramétrables
algo.h:
#ifndef algo_H
nommage.c: #define algo_H
#include <stdio.h> typedef struct noeud {
#include « algo.h » int val;
int main(int argc,char* argv[]) { struct noeud *g,*d;
} ARBRE;
/* … */ typedef struct file {
return 0;
} struct file* premier;
struct file* dernier;
In file included from nommage.c:2: } FILE;
void largeur(ARBRE* a);
algo.h:12: conflicting types for `FILE’
c:/dev-cpp/include/stdio.h:159: previous #endif declaration of `FILE’
● autre exemple: main
● si une bibliothèque foo contient une fonction main de test oubliée…
● solution: avoir un contrôle de portée (foo.toto() par exemple)
– en objet: encapsuler les choses
– avoir des espaces de noms (packages Java)