2. Comment créer une fonction qui renvoie plus d'une valeur ?
Prenons un premier exemple pour faire un rappel sur le principe de fonctionnement d'un fonction :
une fonction possède 0, 1 ou plusieurs paramètres (pour la fonction produit 2 paramètres de type char)
une fonction renvoie au maximum 1 valeurs (ou 0 valeur avec void) grace au mot clé return
Principe général d'une fonction
Code de la fonction qui renvoie une valeur
Appel de la fonction produit
short produit(charn1,charn2){
shortresultat = n1*n2;
returnresultat;
}
int main(){
shortres;
res = produit(10, 5);
printf("res=%d", res);
return 0;
}
En python une fonction peut renvoyer plusieurs valeurs, mais pas en C. Rappelons que python 1.0 a été créé en 1994, alors que le langage C a été créé en 1972 et que le langage C a pour volonté d'être rapide et proche de la machine, ce que ne propose pas python (qui a plein d'autres avantages mais qui est bien moins rapide que le C).
Donc en C, dans une fonction on ne peut pas renvoyer plus d'une valeur avec le mot clé return !
Peut-on renvoyer plus d'une valeur ?
Renvoyer plusieurs valeurs avec le mot clé return est impossible en C
Mais c'est possible en python
defproduit(a, b):
somme = a + b
produit = a * b
return somme, produit
Et pourtant il existe une solution à ce problème :les pointeurs.
Les pointeurs sont très utilisés en C et notamment avec les fonctions. Pourquoi ? Nous l'avons dit, les fonctions peuvent prendre plusieurs paramètres en entrée mais ne peuvent renvoyer qu’un seul paramètre de sortie grace au mot clé return. Ce qui est très limitant. Avec les pointeurs, il est possible de passer l'adresse d'une variable et donc de pouvoir modifier cette variable dans la fonction et qu'elle soit ainsi modifiée dans le main.
Prenons 2 exemples de fonction impossible à coder sans l’utilisation des pointeurs :
la fonction produit qui fait le produit de 2 valeurs de type char, résultat dans une variable de type short(2 octets), la fonction renvoie aussi un code d'erreur si le produit des 2 variables dépasse la taille d'un octet. On a donc 2 variables renvoyée erreur et res. erreur sera renvoyée via le mot clé return et res sera passé en pointeur.
la fonction MinMax qui prend en paramètre un tableau et doit renvoyer le min et le max de ce tableau
Principe général fonction et pointeur
Code de la fonction qui renvoie erreur et utilise un pointeur pour modifier res
Appel de la fonction produit (utilisation de l'adresse de res)
char produit(short*res,charn1, charn2) {
*res= n1 * n2;
int erreur = 0;
if (*res> 127 || *res< -128)
erreur = 1;
return erreur;
}
int main()
{
char erreur;
shortres;
erreur = produit(&res, 100, 5);
printf("erreur=%d, res=%d", erreur, res);
return 0;
}
Principe général fonction et pointeur
Code de la fonction qui modifie min et max
Appel de la fonction produit (utilisation de l'adresse de min et max).
Lorsque l’on passe un tableau à une fonction, on passe l’adresse du début du tableau. Donc comme pour les pointeurs passés en argument, les éléments du tableau peuvent être modifiés dans une fonction.
Prenons un exemple avec 3 fonctions de modification de tableau utilisant soit le formalisme tableau soit le formalisme pointeur. Ces 3 fonctions modifient les valeurs du tableau passé en argument de l'indice 0 à l'indice taille qui est le deuxième élément à être passé à la fonction. La fonction main permet de modifier les éléments du tableau pour les 5 premiers éléments pour la première fonction, puis pour les 4 premiers éléments et enfin pour les 3 premiers éléments pour modif_tab3. La fonction affiche_tab affiche les 5 éléments du tableau aprés chaque modification.
Programme de test
Résultat
voidmodif_tab1(inttab[], inttaille) {
for (int i = 0; i < taille; i++)
tab[i] = 11;
}
voidmodif_tab2(int* tab, inttaille) {
for (int i = 0; i < taille; i++)
*(tab++) = 12;
}
voidmodif_tab3(int* tab, inttaille) {
for (int i = 0; i < taille; i++)
tab[i] = 13;
}
voidaffiche_tab(inttab[], inttaille) {
for (int i = 0; i < taille; i++)
printf("%2d ", tab[i]);
printf("\n");
}
int main() {
int tab[5] = { 1,3,10,20,2 };
affiche_tab(tab, 5);
modif_tab1(tab,5);
affiche_tab(tab, sizeof(tab)/sizeof(int));
modif_tab2(tab, 4);
affiche_tab(tab, 5);
modif_tab3(tab, 3);
affiche_tab(tab, 5);
}
1 3 10 20 2
11 11 11 11 11
12 12 12 12 11
13 13 13 12 11
4. Fonctions et chaines de caractères
Nous avons vu que la différence entre une chaine de caractères et un tableau d’entier ou de réels est liée à la valeur 0 ou '\0' que l’on trouve en fin de chaine de caractères et qui permet de connaitre la taille de la chaine. Ainsi les fonctions associées aux chaines de caractères n’ont pas besoin de 2 arguments (le tableau et la taille du tableau) mais seulement du tableau de caractères.
Prenons un exemple utilisant 3 fonctions de recopie de chaine de caractères codés avec le formalisme tableau et pointeurs. Pour ces 3 fonctions qui recopient caractère par caractère la chaine src vers dest, remarquez l'ajout aprés la boucle de recopie du '\0' terminal qui n'est pas recopié dans la boucle.
Exemple de code de copie de chaine de caractères
Résultat
voidstr_copy(chardest[], charsrc[]) {
int i;
for (i = 0; src[i] != '\0'; i++)
dest[i] = src[i];
dest[i] = '\0';//ajout du 0 terminal qui n'a pas été copié dans la boucle
}
voidstr_copy1(chardest[], charsrc[]) {
int i = 0;
do {
dest[i] = src[i];
i++;
} while (src[i] != '\0');
dest[i] = '\0';//ajout du 0 terminal qui n'a pas été copié dans la boucle
}
voidstr_copy2(char* dest, char* src) {
do {
*(dest++) = *(src++);
} while (*src != '\0');
*dest = '\0';//ajout du 0 terminal qui n'a pas été copié dans la boucle
}
int main() {
char test1[20] = "bonjour";
char test2[20];
str_copy(test2, test1);//copie test1 vers test2
puts(test2);
str_copy1(test1, "au revoir");//copie "au revoir" dans test1
readSizeWave qui prend en argument le nom du fichier à ouvrir et renvoie le nombre d'échantillons aprés l'avoir lue à l'emplacement 20 du header
readWave qui prend en argument le nom du fichier, la taille du tableau et va renvoyer le header et les données (sous formes de tableau),
writeWave qui prend en argument les mêmes arguments que ceux de readWave.
L'avantage de découper notre programme en 3 fonctions est double : pouvoir ouvrir un fichier, lire ses échantillons, les modifier et écrire dans un autre fichier, ce qui n'était pas possible avant et rendre la lecture du main plus simple puisqu'il sera constitué de 3 fonctions seulement (et du code modifiant le programme) et un programme simple à lire et simple à modifier (et donc à maintenir).
intreadSizeWave(char* fichier) {
FILE* file;
int taille;
// Ouvrir le fichier en mode lecture modification binaire
file = fopen(fichier, "rb");
if (file == NULL) {
printf("Impossible d'ouvrir le fichier.\n");
return -1;
}
// Aller à la position de la taille des datas (40 octets)