620 likes | 758 Views
Programmation C. Dominique Rossin dominique.rossin@m4x.org. Première partie. Généralités sur la syntaxe C. Historique de C. AT&T Bell Labs 1969 : Premiere Unix 1973 Unix écrit en C B.Kerninghan, D.Ritchie, The C programming Langage, Prentice Hall, 1978
E N D
Programmation C Dominique Rossin dominique.rossin@m4x.org
Première partie Généralités sur la syntaxe C
Historique de C • AT&T Bell Labs 1969 : Premiere Unix • 1973 Unix écrit en C • B.Kerninghan, D.Ritchie, The C programming Langage, Prentice Hall, 1978 • C ANSI = C ISO (1990) = C« Standard » • Kerningham & Ritchie, the C programming Langage second edition, Prentice Hall, 1988 • SP Harbison, GL Stelle jr, C-A reference Manual, fourth edition, Prentice Hall 1995
Caractéristiques de C • Compilé • Typé avec des types structurés • Limité à des primitives de base mais accompagné d’une librairie standard pour les copies complexes, les entrées/sorties, la gestion de la mémoire … • Interfaçage possible avec tous les langages • ATTENTION • Langage laxiste à la compilation • Débogage parfois difficile
Différences entre C et Java • Java a repris une grande partie de C mais : • C est compilé et utilise un préprocesseur • Certains mots clés changent de définition • Pas d’objets en C, juste des structures • Gestion explicite de la mémoire en C • En C on demande une taille n de mémoire • En java news’occupe de cette opération • Il faut libérer la mémoire utilisée quand on utilise plus une structure de données • En Java, le ramasseur de miettes (GC)s’en occupe • En C il faut toujours déclarer avant d’utiliser
Exemple très simple en Java • Fichier Bonjour.java public class Bonjour { public static void main(String args[]) { System.out.println("Bonjour "); } // fin de main } // fin de la classe • On peut avoir plusieurs fonctions main • l'exécution du programme commence par l'appel à la fonction main du programme appelé.
Un exemple très simple en C • Fichier Bonjour.c (le nom importe peu) #include <stdio.h> int main(void) { printf("Bonjour\n"); return 0; } /* fin de main */ • Il ne peut y avoir qu'une seule fonction main par programme • Elle doit retourner un int (code d'erreur Unix)
Mise en œuvre • En Java • Compilation : javac Bonjour.java • produit un fichier Bonjour.class (bytecode) • commande java Bonjour : • Interprétation de Bonjour.main(String args[]) • En C • prétrait., compilation, éd. de liens : gcc Bonjour.c • produit un fichier a.out (code machine exécutable) • commande ./a.out : exécution de la fonction main
Deuxième exemple • Avec : • formatage des impressions • options de compilation
public class PGCD { public static void main(String args[]) { int a = 257, b = 381; if (a > 0 && b > 0) { System.out.println(" PGCD("+a+","+b+")"); while (a != b) { if (a < b) b = b-a; else a = a-b; System.out.println("=PGCD("+a+","+b+")"); } System.out.println("= "+a); } } }
#include <stdio.h> int main(void) { int a= 257, b = 381; if (a > 0 && b > 0) { printf(" PGCD(%3d,%"d)\n",a,b); while (a != b) { if (a < b) b = b-a; else a = a-b; printf("=PGCD(%3d,%3d)\n",a,b); } printf("=%d\n",a); } return 0; }
Exemple de formatage avec printf PGCD(257,381) "PGCD(%3d,%3d)" =PGCD(257,124) =PGCD(133,124) =PGCD( 9,124) =PGCD( 9,115)
Options de compilation • Prétraitement, compilation et édition de liens : gcc –Wall –g pgcd.o –lm –o pgcd • l'option –Wall demande une compilation avec des diagnostics sur la propreté du code • l'option –g demande que la table des symboles soit ajoutée à l'exécutable • l'option –lm demande de lier la librairie mathématique • l'option –o pgcd demande que le résultat (l'exécutable) soit nommé pgcdau lieu de a.out
Déclaration de variables • Syntaxe, exemples: • id_type id_variable; • id_type id1,id2; • id_type id = valeur; • Les variables locales ne sont pas initialisées implicitement • Les déclarations ne peuvent être qu'en début de bloc • Des constructions comme for(int i = …)et similaires sont interdites
Types élémentaires • La représentation n'est pas normalisée : • char : entiers, codés sur 8 bits • int : généralement codés sur 32 bits, • float : généralement codés sur 32 bits (IEEE) • double : généralement codés sur 64 bits En C, une affectation peut faire perdre implicitement de la précision. On a le droit d'écrire int i = 1.5;
Qualificatifs short et long • short int (ou short simplement) : 16 bits • long int (ou long simplement) : 32 ou 64 • long long int : généralement 64 bits • long double : au moins 64 bits
Conversion de types arithmétiques implicites • En cas de +,*,<,… sur deux types différents: • un opérant est long double => long double • un opérant est double => conversion en double • un opérant est float => conversion en float • char et short int sont convertis en int • En cas de = d'une valeur de mauvais type : conversion vers le type de la variable
sizeof • pseudo-opérateur : sizeof(type) ou sizeof(expr) • donne la taille en octets : sizeof(char)=1 • nombre entier, calculé à la compilation • "retourne" une valeur de type size_t (int ou long ?) • incorrect : • printf("%d\n",sizeof(…)); //suppose int • printf("%ld\n",sizeof(…)); // suppose long • correct • printf("%ld\n",(long)sizeof(…));
Représentation des types numériques sizeof selon les systèmes char, short, int,long, long long, float, double, long double
Qualificatifs signed et unsigned • pour les types entiers seulement, • modifient le domaine représenté mais pas la taille de la représentation, • signed char : un octet : -128..127 • unsigned char :un octet :0..255 • char seul : signed ou unsigned ??? • int est implicitement signed • changent la sémantique des opérations et affectations
Valeurs booléennes • En C, il n'y a pas de type spécifique aux booléens, • on utilise les int • false est représenté par 0 • pas de valeur particulière pour true • test indicateur : printf("%d\n",sizeof('a'=='b')==sizeof(int));
Tests, attention !!!! • En C, on a le droit d'écrire : int i; … if (i = 0) … affecte 0 à i et vaut 0, ie. faux qui est très différent de : …if (i == 0) … compare 0 à i et vaut vrai ou faux selon le cas
Définition de constantes • 3 moyens en C • #define • const • enum
#define • exemples • #define N (1 << 4) • #define DEUX_N ((N)+(N)) • Niveau préprocesseur = perte d'information • usage très répandu • attention aux parenthèses
const • Exemples : • const int N = 1 << 4; • const int DEUX_N = N+N; • fondalementalement une déclaration de variable, • symboles utilisables par le débogueur • l'affectation est en principe interdite, • avec certains compilateurs, on a un simple warning
Enumérations • Pour disposer de constantes symboliques entières sans #define • Déclaration enum nom_d_enum { const1,const2,…}; • Déclaration de variables enum nom_d_enum nom_de_variable
Fonctions • Déclaration obligatoire avant toute utilisation • Récursivité à la Java • void = absence d'argument ou de valeur en retour • Exemples :
#include <stdio.h> #define DEBUT 0 #define FIN 10 static int carre(int); static int cube(int n) {return carre(n)*n;} static int carre(int n) {return n*n;} int main(void) { int i; for (i = DEBUT; i < FIN; i++) printf("%3d^2=%3d,%3d^3=%3d\n",i,carre(i),cube(i)); return 0; }
gcc –Wall –g carre.c –o carre • ./carre 0^2= 0, 0^3= 0 … 5^2= 25, 5^3=125 6^2= 36, 6^3=216 7^2= 49, 7^3=343
Les boucles et tests • Même syntaxe qu'en Java • Les boucles for, while et do…while • while (condition) {…} • do {…} while (condition) • for (i = 0; i < 10; i++) {…} • Les structures de tests • if (condition) {} else {}
Tableaux statiques et chaines de caractères • Déclaration et allocation type_des_elements nom_tab[nombre]; Initialisation (+declaration et allocation) : int primes[]={2,3,5,7,11}; // int primes[6] char s[] = "abc"; /*char s[4]; s[0]='a'; s[1]='b';s[2]='c';s[3]='\0'*/ Les indices de tableau vont de 0 à N-1 mais IL N'Y A PAS DE VERIFICATION DE DEBORDEMENT
Copie d'un tableau ou d'une chaine void copie_tableau(char []t1,char []t2,int n) { while (n --> 0) t1[n]=t2[n]; } void copie_chaine(char []s1,char []s2) { int i=0; do s2=s1[i] while (s1[i++]!='\0'); }
Fonctions simples sur les chaines • strlen(s) : retourne la longueur de la chaine s, sans compter le nul. • Il faut mettre #include <string.h> • strcmp(s1,s2): compare deux chaines et retourne un nombre <0,=0,>0. • atoi(s),atol(s),atof(s) : conversion vers entier, long et double • Librairie stdlib.h
Structures • Déclaration struct nom {type1 c1; type2 c2;…}; • Déclaration de variable : struct nom variable; • Exemple • struct evt {int jour,mois,annee; char evt[50]}; • struct evt anniv; • anniv.jour = 14; anniv.annee=1973; • strcpy(anniv.evt,"Anniversaire de Dominique");
Exemple #include <stdio.h> #include <math.h> struct point {float x; float y} p10 = {0,10}; struct rect {struct point p1; struct point p2}; static double sqr(double x) { return x*x; } static double dist(struct point p1,struct point p2){ return sqrt(sqr(p1.x-p2.x)+sqr(p1.y-p2.y)); }
int main(void) { struct rect r = {{0,5},{25,20}}; return 0; }
Format d'affichage printf(format, arg1, arg2,…); format = chaine de caractères : • %c : caractère • %d : entier • %f : float • %s : chaine de caractères • %u : entier non signé char msg_erreur[20][100]; void erreur(int no) { printf("erreur numero %d:%s",no,msg_erreur[no]); }
Divers • Types synonymes : typedef type nom_nouveau_type; typedef struct {int re; int im;} Complexe; Complexe z;
Macros avec arguments #define mauvais_carre(x) x*x #define carre(x) ((x)*(x)) #define max(a,b) ((a)<(b)?(b): (a)) max(mauvais_carre(x+1),carre(x+1)); ((x+1*x+1)<(((x+1)*(x+1)))?…
Références • La référence : • Kerningham, Ritchie, The C Programming Language, second edition, Prentice Hall, 1988
Programmation C Deuxième Partie
Pointeurs et adresses • Mémoire = Collection de cellules numérotées consécutivement et contenant chacun un octet • Adresse = Localisation d'une variable dans la mémoire • Pointeur = variable contenant l'adresse d'une variable • Typage d'es pointeurs : T* est le type "adresse d'un objet de type T". • Objectifs: • Passage par référence pour modifier un paramètre • efficacité dans la transmission des paramètres et du résultat • référencer des objets dont la localisation est inconnue à la compilation
Référencement et déréférencement &var est l'adresse de (une référence à) la variable var *ptr est la variable pointée par le pointeur ptr. int age; printf("entrez votre age : "); scanf("%d",&age); int i=2,j=5; static void echanger(int *p1,int *p2) { int t=*p1; *p1 = *p2; *p2 = t; } echanger(&i,&j); /* i==5 et j==2*/
L'adresse NULL Un pointeur dont la valeur est ((void *) 0), noté par la constante symbolique NULL ne pointe vers aucune cellule mémoire DEREFERENCER NULL provoque une erreur !!!! int main(void) { int *p; printf("%p,%p\n",p,NULL); return 0; } 0xbffffc58,(nil)
100 104 108 112 116 120 124 x y z[0] z[1] z[2] z[3] ip ip = & x; /* ip pointe sur x*/ y = *ip; /* y==1*/ *ip = 0; /* x==0*/ int x = 1, y=2, z[4], *ip; *ip = *ip+10; /*x==10*/ ip = &z[2]; /*ip pointe sur z[2]*/ ip=&y; /*ip pointe sur y*/ ++*ip; /*y==2*/ (*ip)++; /*y==3*/ 1 2 0 1 100 1 2 100 1 1 100 10 2 104 10 1 100 10 1 116 10 1 104 10 3 104
Tableaux et pointeurs Le nom d'un tableau t s'évalue à l'adresse de t[0] Si un pointeur p pointe sur t[0], p+i pointe par définition sur t[i] Ainsi,après int t[N], *p=t; /*p==&t[0]*/ on a : p+i == &t[i], *(p+i) == t[i], et même t+i == &t[i], *(t+i) == t[i] Mais on ne peut pas affecter à un nom de tableau
Usage idiotique des pointeurs static int somme(int *t, int n) { int i,s; for(s=0,i=0; i < n; i++) s = s+t[i]; return s; } est équivalent à : static int somme(int *t, int n) { int s = 0; while (n-- > 0) s+=*t++; /* lire *(t++) */ return s; }
Déréférencements et type struct ptr->champ = (*ptr).champ typedef struct {int re; int im;} Complexe; Complexe *addition(Complexe *a,Complexe *b) { b->re=a->re+b->re; b->im=a->im+b->im; return b; } /* permet addition(u,addition(v,w)) */
Liste chaînées typedef struct cell { int val; struct cell *suivant; } cell,*liste; void affiche(liste l) { for (;l;l=l->suivant) printf("%d, ",l->val); }
Allocation dynamique malloc void *malloc(size_t) Permet la réservation de mémoire à l'exécution pour une variable inconnue à la compilation. malloc(n) renvoie: • l'adresse de début d'un bloc de n octets • NULL si plus aucune mémoire n'est disponible Schéma typique : /*typ p[N]; dynamique*/ typ *p=(typ *) malloc(N*sizeof(typ)); if (!p) erreur(…)