Jean-Jacques BOURDIN
While lots of programmers don't apply that rule, lots of programmers
spend more time than necessary on debugging.
Si cette règle n'est pas appliquée, ne vous étonnez pas de passer
plus de temps en debugging que les autres...
#include <stdio.h> void etalors (int * pa, int *pb) { *pa = *pa + *pb; *pb = *pa - *pb; *pa = *pa - *pb; } int main () { int x, y; x = 5; y = 8; printf("Avant \t x : %d\t y %d\n", x, y); etalors(&x, &y); printf("Apres \t x : %d\t y %d\n", x, y); } |
#include <stdio.h> enum sema {lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche}; enum week {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; void affiche_day (enum week d) { switch(d) { case Sunday: printf("Sunday "); break; case Monday: printf("Monday "); break; case Tuesday: printf("Tuesday "); break; case Wednesday: printf("Wednesday "); break; case Thursday: printf("Thursday "); break; case Friday: printf("Friday "); break; case Saturday: printf("Saturday "); break; } } void affiche_dday (enum week d) { switch((int) d) { case 0: printf("Sunday "); break; case 1: printf("Monday "); break; default: printf("otherday "); break; } } void affiche_jour (enum sema j) { switch(j) { case lundi: printf("lundi "); break; case mardi: printf("mardi "); break; case mercredi: printf("mercredi "); break; case jeudi: printf("jeudi "); break; case vendredi: printf("vendredi "); break; case samedi: printf("samedi "); break; case dimanche: printf("dimanche "); break; } } int main () { enum sema jour; enum week day; for (jour = lundi; jour <= dimanche; jour++) { affiche_jour(jour); day = (enum week) jour + 1; affiche_day(day); affiche_dday(day); printf("\n"); } jour = vendredi; printf("\n\n\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); affiche_jour(jour++); printf("\n"); } /* Blanc14.local: a.out lundi Monday Monday mardi Tuesday otherday mercredi Wednesday otherday jeudi Thursday otherday vendredi Friday otherday samedi Saturday otherday dimanche otherday vendredi samedi dimanche */ |
#include <stdio.h> enum boole {vrai, faux}; enum boole non (enum boole v) { switch (v) { case vrai : return faux; case faux : return vrai; } } enum boole et (enum boole v, enum boole w) { if (v == faux) return faux; if (w == faux) return faux; return vrai; } enum boole ou (enum boole v, enum boole w) { if (v == vrai) return vrai; if (w == vrai) return vrai; return faux; } void afficheb (enum boole v) { if (v == vrai) printf("vrai"); else printf("faux"); printf(" "); } int main () { enum boole a, b, c, d, e, f; printf(" a\t b\t c\t et\t ou\t b+1\n"); f = 0; for (a = vrai; a <= faux; a = a + 1) { c = non (a); for (b = vrai; b <= faux; b = b + 1) { d = et (a, b); e = ou (a, b); f = f + 1; printf("%2d\t %2d\t %2d\t %2d\t %2d\t %2d\t \n",a, b, c, d, e, f); } } f = 0; printf("\n a\t b\t c\t et\t ou\t b+1\n"); for (a = vrai; a <= faux; a = a + 1) { c = non (a); for (b = vrai; b <= faux; b = b + 1) { d = et (a, b); e = ou (a, b); f = f + 1; afficheb(a); afficheb(b); afficheb(c); afficheb(d); afficheb(e); afficheb(f); printf("\n"); } } printf(" Taille de cet enum : %d\n", (int) sizeof(enum boole)); } |
As you can see, the enum valeur is just a number, therefore that
will not improve your program.
Vous pouvez remarquer que les enums ne sont que des nombres, dès lors
ce n'est pas ainsi que vous pourrez accélérer vos programmes.
int somvec (int n) { int t [100]; int i; t [0] = 0; if (n > 99) { return -1; } for (i=1; i <= n; i++) { t[i] = t[i-1] + i; } return t[n]; } int main () { int v, res; v = 5; res = somvec(v); printf("som(%d)=%d\n",v,res); v = 6; res = somvec(v); printf("som(%d)=%d\n",v,res); v = 7; res = somvec(v); printf("som(%d)=%d\n",v,res); } |
#include<stdio.h> void remplir (float vec [100], int nb) { float x, eps; int i; if (nb >= 100) return; x = 1.5; eps = 0.173; for (i = 0; i < nb; i = i + 1) { vec [i] = x; x = x + eps; if (x > 2.0) eps = 0.01 - eps; if (x < 0.5) eps = 0.015 - eps; } } void voirvec (float vec [100], int nb) { int i; if (nb >= 100) return; for (i = 0; i < nb; i = i + 1) { printf("vec[%3d] == %f\n",i, vec [i]); } } int main () { int n; float vec [100]; n = 20; remplir(vec, n); voirvec(vec, n); } |
vec[ 0] == 1.500000 vec[ 1] == 1.673000 vec[ 2] == 1.846000 vec[ 3] == 2.019000 vec[ 4] == 1.856000 vec[ 5] == 1.693000 vec[ 6] == 1.530000 vec[ 7] == 1.367000 vec[ 8] == 1.204000 vec[ 9] == 1.041000 vec[ 10] == 0.878000 vec[ 11] == 0.715000 vec[ 12] == 0.552000 vec[ 13] == 0.389000 vec[ 14] == 0.567000 vec[ 15] == 0.745000 vec[ 16] == 0.923000 vec[ 17] == 1.101000 vec[ 18] == 1.279000 vec[ 19] == 1.457000 |
Another function, with integers this time.
Autre fonction de remplissage d‘un vecteur, d‘entiers cette fois.
Nouvelle version, à peine améliorée, du 5 février 2024.
void remplirv (int nb, int vec[100]) { int crt, pre, new, i; crt = 13; pre = 21; vec[0]=pre; vec[1]=crt; for (i=2; i <= nb; i = 1 + i) { new = (crt + pre) % 23; // reste de la division vec[i] = new; pre = crt; crt = new; } } |
Refaire tous les exercices itératifs en remplissant un vecteur au fur et à mesure et en utilisant les valeurs déjà dans le vecteur pour calculer les nouvelles, sur l‘exemple de somvec ci-dessus.
Puis coninuez en remplissant le vecteur avec la fonction remplirv ci-dessus et faites :
vecteur initial : [8, 13, 21, 11, 9, 20, 6, 3, 9, 12]
Vecteur final : [21, 20, 13, 12, 11, 9, 9, 8, 6, 3]
/* jjb */ void replaceminparmaj (int nbc, char txt [1000]) { int i, diff; if (nbc > 999) { printf("\n\ntrop de texte\n\n"); return; } for (i = 0; i < nbc; i++) { diff = txt[i] - ‘a‘; if (diff >= 0) { if (diff < 26) { txt[i] = (char) (‘A‘ + diff); } } } } void affitxt (int nbc, char t [1000]) { int i; for (i= 0; i < nbc; i++) { printf("%c",t[i]); } printf("\n"); } void remplirtxt (int nbc, char t [1000]) { int i; for (i=0; i < 50; i++) { t[i] = (char) 'a' + (i % 26); } for (i=50; i < 60; i++) { t[i] = ' '; } for (i=60; i < 110; i++) { t[i] = (char) 'A' + (i % 26); } for (i=110; i < nbc; i++) { t[i] = ' '; } } /* on peut utiliser ce remplissage aussi */ int remplirchaine(char ch [1000]) { ch [0] = 'J'; ch [1] = 'e'; ch [2] = ' '; ch [3] = 'F'; ch [4] = 'a'; ch [5] = 'i'; ch [6] = 's'; ch [7] = ' '; ch [8] = 'S'; ch [9] = 'o'; ch [10] = 'u'; ch [11] = 'v'; ch [12] = 'e'; ch [13] = 'n'; ch [14] = 't'; ch [15] = ' '; ch [16] = '\0'; return 16; } int main () { int nbc; char t [1000]; nbc = 120; remplirtxt(nbc,t); affitxt(nbc, t); replaceminparmaj(nbc, t); affitxt(nbc, t); nbc = remplirchaine(t); affitxt(nbc, t); replaceminparmaj(nbc, t); affitxt(nbc, t); } } |
Just try it.
Il ne reste plus qu'à essayer :
neige: gcc ex16.c neige: a.out abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF Je Fais Souvent JE FAIS SOUVENT |
As one might see, you always associate the number of elements of a
vector to the vector itself when it is passed as argument.
Remarquez bien que quand on veut transmettre un vecteur comme argument
il est très important de transmettre aussi le nombre d‘éléments
utilisés de ce vecteur.
void remplirc (int nbc, char vc [1000]) { int i, j, k; char c; c = 'a'; j = 3; k = 1; for (i = 0; i < nbc; i = i + 1) { vc[i] = c ; c = c + j; if (c > 'z') { c = ' '; } else { if (c < 'a') { c = 'a' + k; k = k + 1; } } if (k > 10) { k = 2; } } } void affichechars (int nbc, char vc [1000]) { int i; printf(" Les caracteres sont\n"); for (i = 0; i < nbc; i = i + 1) { printf ("%c",vc[i]); } printf("\n"); } |
We‘ll need typedef to help us understand.
Nous rajoutons ici la commande "typedef" qui permet de définir
un nouveau type données à partir des types déjà connus.
typedef float tablef [100];
Et, au lieu d‘écrire partout float t [100] on écrira
tablef t.
tablef tab [100]; |
typedef tablef table [100]; |
void voirt (table t, int taillex, int tailley) { int i, j; for (i = 0; i < taillex; i = i + 1) { for (j = 0; j < tailley; j = j + 1) printf("%5.2f ", t[x][y]); printf("\n"); } } |
/* exo sur les tableaux a trois dimensions */ typedef float table3 [50] [50] [50]; void remplirt (table3 t, int sx, int sy, int sz) { int x, y, sxy, z; float v, eps, add, pas; pas = 0.5; v = pas; add = 5.0; sxy = sx * sx + sy * sy; eps = 1.0 / (float) (sxy); for (x = 0; x < sx; x += 1) { for (y = 0; y < x; y += 1) { for (z = 0; z < sz; z += 1) { t[x][y][z] = (float) (x * x - y * y + z + add) * eps; } } for (y = x; y < sy; y += 1) { for (z = 0; z < sz; z += 1) { t[x][y][z] = (float) ( y * y - x * x + z + add) * eps; } } add += v; if (add > 10.0) v = - pas; if (add < 0.1) v = pas; } } |
struct duo { int quot; int rest; } ; typedef struct duo duo; |
duo division (int a, int b) { duo d; d.quot = 0; d.rest = 0; if (b == 0) return d; while (a >= b) { a = a - b; d.quot = d.quot + 1; } d.rest = a; return d; } |
int main () { duo res; int a, b; res = division(a,b); printf(" when dividing %d by %d, one gets a result", a, b); printf(" of %d with a reminder of %d\n", res.quot, res.rest); } |
All the type definitions are to be placed at the start of the file, in the "head" of the file.
On place toujours la définition en tête de fichier
Example:
Nous mettons en place une structure de vecteur de nombres entiers.
struct vecint { int nbele; int vec[100]; }; typedef struct vecint vecint_t; |
vecint_t remplirv (int nb) { int crt, pre, new, i; vecint_t w; crt = 13; pre = 21; w.vec[0]=pre; w.vec[1]=crt; for (i=2; i <= nb; i = 1 + i) { new = (crt + pre) % 23; // reste de la division w.vec[i] = new; pre = crt; crt = new; } w.nbele = nb; return w; } |
Refaire quelques exercices de vecteurs d‘entiers avec cette structure.
New example altitudes array:
exemple tableau d‘altitudes: Vous trouverez ci-dessous une déclaration permettant de travailler sur des tableaux d‘altitudes, avec non seulement les valeurs, mais aussi les largeurs et hauteurs de la table.
We‘ll work on altitude tables. Each cell contains the altitude at
its coordinates.
Nous allons travailler avec des tableaux d‘altitude. Sur une grande
carte, carrée, nous donnons l‘altitude de chacune des cases,
c‘est-à-dire l‘altitude moyenne pour toute la surface
qu‘elle représente sur le terrain.
Pour générer le tableau, il faut utiliser cette fonction en donnant
comme paramètres, le tableau, le nombre de colonnes et le nombre de
lignes :
/* exo sur les altitudes JJ’2023 */ typedef struct table_alt { int larg; int haut; float t[100][100]; } table_alt_t; /* desormais table_alt_t est un type qui contient - deux valeurs de taille et - un vecteur float [100][100]*/ |
void affiche (table_alt_t t) { int x, y, sx, sy; sx = t.larg; sy = t.haut; printf("Tailles max %d et %d\n", t.larg, t.haut); for (x = 0; x < sx; x = x + 1) { for (y = 0; y < sy; y = y + 1) printf("%5.2f\t", t.t[x][y]); printf("\n"); } } |
table_alt_t remplirt (int sx, int sy) { int x, y, sxy; float v, eps, add, pas; table_alt_t tt; tt.larg = sx; tt.haut = sy; pas = 0.5; v = pas; add = 5.0; sxy = sx * sx + sy * sy; eps = 100.0 / (float) (sxy); for (x = 0; x < sx; x += 1) { for (y = 0; y < x; y += 1) { tt.t[x][y] = (float) (x * x - y * y + add) * eps; } for (y = x; y < sy; y += 1) { tt.t[x][y] = (float) ( y * y - x * x + add) * eps; } /* for (y = 0; y < sy; y = y + 1) printf("%f \t", (*tt).t[x][y]); printf("\n"); */ add += v; if (add > 10.0) v = - pas; if (add < 0.1) v = pas; } return tt; /* ici on renvoie la structure */ } |
x == y * y
If x≥0 then 0≤ y <x+ 1.
Commençons
par cette constatation : si ce nombre, x, est positif ou nul
alors la racine est comprise entre 0 et x+1, toujours.
Let‘s call inf and sup these values (0 and
x+1). Let‘s call med the average of them. it is easy to find
if y is greater than med or not. If y>med then y belongs to
]med,sup], if not, y belongs to [inf, med]. One just has to set one of
the two values inf or sup and carry on, and on, and on...
When does it stop? When the value is close enough to the square
root. Close enough is when the difference is smaller than a given value.
Mais entre ces deux valeurs, inf et sup, il y en a
beaucoup. Coupons par le
milieu, mil. Comment savoir si la racine est plus grande ou
plus petite que mil ? Et si elle est plus petite, ne
pouvons-nous pas réduire l‘intervalle de recherche ? De même si
elle est plus grande.
Vous pouvez faire tourner longtemps cet algorithme, mais attention,
vous n‘avez pas besoin d‘une infinité de chiffres, il faut donc vous
arrêter quand la précision requise est acquise !
Write this function square root.
Faites une fonction qui calcule ainsi la racine carrée d‘un nombre positif à virgule.
As seen in class a structure for complex numbers can be:
struct complex { float real; float imag; }; typedef struct complex complex; |
struct complex mulc (struct complex a, struct complex b) { struct complex res; res.real = a.real + b.real; res.imag = b.imag + a.imag; return res; } |
Donnez une fonction qui permet de comparer en grandeur deux nombres complexes (on compare en fait leur module).
If we add this definition to bring a vector of complex numbers:
struct vecc { int nbe; complex tab [100]; }; typedef struct vecc vecc_t ;this is a function to create such a vector:
/* pour travailler les structures */ vecc_t remplirc (int nbc) { int i; vecc_t t; float x, y; t.nbe = nbc; x = -4.0; y = nbc / 10.0 + 1.; for (i = 0; i < nbc; i++) { t.tab[i].real = x; t.tab[i].imag = y; x = x + 0.35; if (x > -3.7) { x -= 1.67; } y = y - 0.29; if (y < 2.0) { y += 0.6; } } return t; } void affic (vecc_t t) { int i; printf("Voici les %d nombres complexes\n", t.nbe); for (i= 0; i < t.nbe; i++) { printf("(%6.3f,%6.3f) ", t.tab[i].real, t.tab[i].imag); } printf("\n"); } int main () { vecc_t ta; int n; n = 10; ta = remplirc(n); affic (ta); ta.tab[2] = addc(ta.tab[0],ta.tab[1]); ta.tab[3] = multc(ta.tab[0],ta.tab[1]); affic (ta); } |
/* pour s amuser avec des unions */ typedef unsigned short ushort; typedef unsigned long ulong; union etrange { ushort sh; ulong lg; char c; } ; typedef union etrange strange_t; void voir (strange_t s) { printf("short %d\t long %d\t char %d\t car %c \n", s.sh, s.lg, (int) s.c, s.c); } int main () { strange_t v; v.lg = 1024; voir (v); v.lg = 1024 * 1024; voir (v); for (v.lg = 1; v.lg < 128; v.lg++) voir (v); } /* short 1024 long 1024 char 0 car short 0 long 1048576 char 0 car short 1 long 1 char 1 car short 2 long 2 char 2 car short 3 long 3 char 3 car short 4 long 4 char 4 car short 5 long 5 char 5 car short 6 long 6 char 6 car short 7 long 7 char 7 car short 8 long 8 char 8 car short 9 long 9 char 9 car short 10 long 10 char 10 car short 127 long 127 char 127 car La suite, vous la verrez en faisant tourner le programme ! */ |
Nous allons maintenant créer plusieurs fichiers et les compiler
séparément.
Le premier fichier contient les prototypes des fonctions. Il
s‘agit du fichier "fac.h":
/* les prototypes des fonctions */ int fac (int); int fac_acc (int, int); void affiche (int, int); |
Ensuite nous avons un fichier qui s‘occupe des entrées sorties, le fichier "io.c":
#include "fac.h" void affiche (int a, int b) { printf("Vous avez donne \t%d\n",a); printf("Sa factorielle est \t%d\n",b); } |
Le fichier suivant contient la fonction du
calcul de la factorielle, c‘est le fichier "fac.c":
#include "fac.h" /* le calcul de la fonction factorielle */ int fac (int n) { return fac_acc (n, 1); } int fac_acc (int n, int acc) { if (n < 2) { return acc; } else { return fac_acc (n-1, n*acc); } } |
Enfin, le fichier "main.c" contient le reste du code (ici la
fonction main):
#include "fac.h" main () { int n, f; n = 5; f = fact(n); affiche(n,f); n = 6; f = fact(n); affiche(n,f); } |
Il reste à les compiler séparément. Ceci se généralise grâce au fichier "Makefile":
# Compilation generique par Bourdin CC=gcc fac: io.o main.o fac.o fac.h $(CC) io.o main.o fac.o -o fac main.o: main.c fac.h io.o: io.c fac.h fac.o: fac.c fac.h clean: @rm -f *.o @rm -f core @rm -f *~ |
Attention, il faut remettre des "tabulations" à la main, sous emacs.
Dans ce fichier on trouve trois sortes de lignes :
alpha5: make fac predecessor cycle (io.o) gcc -O -c io.c predecessor cycle (main.o) gcc -O -c main.c predecessor cycle (fac.o) gcc -O -c fac.c gcc io.o main.o fac.o -o fac ld: ERROR 33: Unresolved text symbol "fact" -- 1st referenced by main.o. Use linker option -v to see when and which objects, archives and dsos are loaded. ld: INFO 152: Output file removed because of error. *** Error code 1 (bu21) |
Après correction (la fonction est nommée "fac" et pas "fact"!):
alpha5: make fac predecessor cycle (io.o) predecessor cycle (main.o) gcc -O -c main.c predecessor cycle (fac.o) gcc io.o main.o fac.o -o fac alpha5: fac Vous avez donne 5 Sa factorielle est 120 Vous avez donne 6 Sa factorielle est 720 alpha5: |
On peut améliorer ce fichier de make, en utilisant des variables et des règles :
# Meilleure compilation par Bourdin # # Attention, les tabulations doivent être refaites vous-mêmes, à la # main # OBJ=io.o main.o fac.o CC=gcc fac: $(OBJ) fac.h $(CC) $(OBJ) -o fac %.o : %.c $(CC) -c $< clean: @rm -f *.o @rm -f core @rm -f *~ |
Vous remarquerez que les fichiers qui finissent par ".o" sont obtenus par compilation avec l‘option "-c" c‘est l‘objet des lignes 14 et 15.
Quelques variables par défaut
Vous pouvez regarder la page de mon collègue Bel Hadj pour comprendre mieux : sur les makefile .