Les types réels
● calcul en virgule flottante
⇒ approximations (ne pas utiliser pour des calculs exacts, ex: finances)
● float : réel simple précision
● double : réel double précision
Représentations des réels
● 12.34 ou 1234. ou .1234
● notation mantisse ╳ 10exposant 1723.68 = 1.72368e3 = 17.2368E2 0.015 = 1.5e-2
● par défaut, les constantes sont double 245.45f = 245.45F ⇒ float
245.45 = 245.45l = 245.45L ⇒ double
● environ 7/15 chiffres significatifs pour les float/double
Problèmes de précision
● se méfier beaucoup des float
● se méfier quand même des double
int main(int argc,char* argv[]) { float f=0.f;
double d=0;
int i;
for (i=0;i<1000;i++) { f=f+0.1f;
}
for (i=0;i<10000000;i++) { d=d+0.1;
}
printf(« f=%f\nd=%f\n »,f,d); return 0;
}
$>./a.out f=99.999046 d=999999.999839
● 176.768204+0.00001≠176.768214
● pas toujours de float d=√x tel que d2=x
se méfier de l’égalité sur les réels
int main(int argv,char* argc[]) {
/* racine(31247) */ float d=sqrt(31247); float d_plus_e=d+0.00001; float d_minus_e=d-0.00001; printf(« d-e=%f\t(d-e)^2=%f\n »
» d=%f\t d^2=%f\n »
« d+e=%f\t(d+e)^2=%f\n »
,d_minus_e,d_minus_e*d_minus_e
,d,d*d
return ,d_plus_e,d_plus_e*d_plus_e);
0;
}
$>./a.out
d-e=176.768188 (d-e)^2=31246.992457
d=176.768204 d^2=31246.997852 d+e=176.768219 (d+e)^2=31247.003246
Opérations sur les réels
● + – * comme pour les entiers
● / donne une division réelle
● conversions automatiques:
2 +/-/* 3.5
6/1.5
8.4/2 double a=2; int a=2.45;
2.0 +/-/* 3.5
6.0 / 1.5
8.4 / 2.0 double a=2.0;
arrondi à int a=2;
Piège de conversion
● si on veut un résultat réel pour int/int, il faut une conversion explicite
int main(int argc,char* argv[]) { int a=3;
int b=7;
int c=10;
float average1=(a+b+c)/3; float average2=(a+b+c)/3.0; float div1=a/b;
float div2=(float)a/b;
printf(« %f\n »,average1);
printf(« %f\n »,average2);
printf(« %f\n »,div1);
printf(« %f\n »,div2);
return 0;
}
$>./a.out
6.000000
6.666667
0.000000
0.428571
Le type caractère
● on interprète les char comme des codes de caractères
– pas de problème entre 0 et 127
– au-delà, dépend de l’encodage du système (à éviter)
● on désigne le code du caractère x avec ‘x’
● on peut utiliser les opérateurs entiers
– exemple: ‘a’+1 vaut ‘b’ (car les codes sont bien rangés)
● ne pas confondre caractère et valeur d’un chiffre
/**
* Returns the value of the given digit,
* or -1 if the given character is not a digit * in [0;9].
*/
int value_of_digit(char digit) {
if (digit<‘0′ || digit>’9’) return -1; return digit-‘0’;
}
Caractères spéciaux
● ‘\n’ : saut de ligne
● ‘\r’ : retour au début de la ligne
● ‘\t’ : tabulation (largeur variable) + ‘\\’ et ‘\ »‘: les caractères \ et »
int main(int argc,char* argv[]) {
printf(« aaaa\r »);
printf(« bbb\r »);
printf(« cc\r »);
printf(« d\n »);
printf(« xxxxx\t*\\*\n »);
printf(« yyy\t*\ »*\n »);
return 0;
}
$>./a.out
dcba
xxxxx *\*
yyy * »*
Caractères accentués
● lettres accentuées > 127
● problème de portabilité et d’encodage
– ne marche plus si on change de système et/ou de codage:
int main(int argc,char* argv[]) { printf(« à Marne-la-Vallée\n »); return 0;
}
$>./a.out
Ó Marne-la-VallÚe
Les chaînes de caractères
● pas un type, mais une convention de stockage: tableau de caractères, dont le dernier est ‘\0’
● délimitée par des guillemets char* msg= »Welcome »;
● variantes:
char msg[]= »Welcome »;
char msg[]={‘W’,’e’,’l’,’c’,’o’,’m’,’e’,’\0′};
● chaîne vide « » = tableau dont la première case contient ‘\0’
● longueur = caractères avant ‘\0’ – « abcd » a une longueur de 4
● concaténation automatique des chaînes constantes
int main(int argc,char* argv[]) { int main(int argc,char* argv[]) {
printf(« aaaa\n »); printf(« aaaa\n »
printf(« bbbb\n »); « bbbb\n »
printf(« cccc\n »); return « cccc\n »);
} return 0; 0;
}
● accès au nème caractère:
char* s= »wxyz »;
char c=s[2];
⇒ c vaut ‘y’ car on commence à l’indice 0
● attention au débordement
int main(int argc,char* argv[]) {
char* s= »Hello »;
printf(« %c\n »,s[4657]);
return 0;
}
$>./a.out Segmentation fault
● on ne peut modifier ni les chaînes statiques, ni les arguments de main
int main(int argc,char* argv[]) { char* s= »wxyz »;
s[2]=’u’;
printf(« %s\n »,s);
return 0;
}
int main(int argc,char* argv[]) {
argv[0][0]=’*’;
printf(« %s\n »,argv[0]);
return 0;
}
$>./a.out Segmentation fault
Les variables
● identificateurs: [_a-zA-Z][_a-zA-Z0-9]*
(ce sera vrai aussi pour les noms de fonctions)
hello_89 __tmp 2tree
● éviter ceux qui ressemblent à des mots-clés: new, class…
int main(int argc,char* argv[]) {
int old=7;
int new=7;
printf(« %d\n »,new-old);
$>gcc -Wall -ansi test.c return 0;
}
$>g++ -Wall -ansi test.c
test.c: Dans function « int main(int, char**) »:
test.c:5: erreur d’analyse syntaxique avant « new »
test.c:6: erreur d’analyse syntaxique avantle jeton « – »
● doivent être déclarées avant d’être utilisées
● déclaration: int a;
● avec initialisation: int a=3;
● déclarations multiples: float f1=.3,f2;
● attention: int main(int argc,char* argv[]) {
char* a= »hello »,b= »you »;
printf(« %s %s\n »,a,b);
$>gcc -Wall -ansi test.c return 0;
}
test.c: Dans la fonction « main »:
test.c:4: AVERTISSEMENT: initialisation transforme en
entier un pointeur sans transtypage
Portée d’une variable
● visible dans le bloc
int main(int argc,char* argv[]) {
int i;
for (i=0;i<10;i++) {
int a=i;
}
printf(« a=%d\n »,a);
return 0;
}
● dans une fonction: variable locale
● hors d’une fonction: variable globale