Échange générique
Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 51 sur 61.
Une opération courante en programmation consiste à échanger deux valeurs. Normalement, vous devriez écrire une fonction d'échange distincte pour chaque type — une pour int, une autre pour double, et ainsi de suite. Mais avec void* et memcpy, nous pouvons écrire une seule fonction qui échange n'importe quelles deux variables.
L'idée clé est que l'échange consiste simplement à déplacer des octets. Si vous connaissez la taille des données, vous pouvez copier des octets sans connaître le type réel. La signature de la fonction ressemble à ceci :
void generic_swap(void* a, void* b, size_t size);Le paramètre size indique à la fonction combien d'octets déplacer. À l'intérieur, nous utilisons un tampon temporaire et memcpy de <string.h> pour déplacer les octets :
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // tampon temporaire
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Désormais, cette fonction unique fonctionne pour n'importe quel type :
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x vaut maintenant 10, y vaut maintenant 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p vaut maintenant 2.71, q vaut maintenant 3.14Ce modèle — l'utilisation de void* avec size_t — est la manière dont la bibliothèque standard du C implémente des fonctions génériques comme qsort et bsearch.
Défi
FacileConstruisons un utilitaire d'échange générique capable d'échanger les valeurs de n'importe quelles deux variables, quel que soit leur type — en utilisant la puissance des pointeurs void* et de memcpy.
Vous organiserez votre code sur trois fichiers :
swap.h: Déclarez votre fonctiongeneric_swapqui prend deux pointeursvoid*et un paramètresize_tindiquant la taille des données à échanger. N'oubliez pas les gardes d'inclusion !swap.c: Implémentez la fonctiongeneric_swap. Utilisez un tampon temporaire etmemcpypour déplacer les octets entre les deux emplacements mémoire. Rappelez-vous le modèle d'échange en trois étapes : copier la première valeur vers le tampon temporaire, copier la seconde vers la première, copier le tampon temporaire vers la seconde.main.c: Démontrez le fonctionnement de votre échange générique avec différents types de données. Lisez un indicateur de type (ipour les entiers,dpour les doubles) suivi de deux valeurs. Créez deux variables du type approprié, échangez-les à l'aide de votre fonctiongeneric_swap, puis affichez les deux valeurs après l'échange.
Votre programme recevra trois entrées :
- Un indicateur de type :
ipour un entier oudpour un double - La première valeur
- La deuxième valeur
Après l'échange, affichez les deux valeurs sur des lignes séparées. Pour les doubles, utilisez 2 décimales.
Exemple de sortie lorsque les entrées sont i, 5, et 10 :
10
5Exemple de sortie lorsque les entrées sont d, 3.14, et 2.71 :
2.71
3.14Exemple de sortie lorsque les entrées sont i, -7, et 42 :
42
-7Exemple de sortie lorsque les entrées sont d, 99.99, et 0.01 :
0.01
99.99La beauté de cette approche est que votre fonction generic_swap n'a aucune idée des types qu'elle échange — elle déplace simplement des octets. La même fonction fonctionne pour les entiers, les doubles, les structures ou tout autre type de données. Vous devrez inclure <string.h> pour memcpy et <stddef.h> ou <stdlib.h> pour size_t.
Aide-mémoire
Une fonction d'échange générique utilise des pointeurs void* et memcpy pour échanger les valeurs de n'importe quel type en déplaçant des octets sans connaître le type réel.
Signature de la fonction :
void generic_swap(void* a, void* b, size_t size);Implémentation utilisant un tampon temporaire et memcpy :
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // tampon temporaire
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Utilisation avec différents types :
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x vaut maintenant 10, y vaut maintenant 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p vaut maintenant 2.71, q vaut maintenant 3.14En-têtes requis : <string.h> pour memcpy et <stddef.h> ou <stdlib.h> pour size_t.
Essayez vous-même
#include <stdio.h>
#include "swap.h"
int main() {
char type;
scanf("%c", &type);
if (type == 'i') {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
// TODO: Appeler generic_swap pour échanger a et b
// Astuce : Utiliser &a, &b, et sizeof(int)
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: Appeler generic_swap pour échanger a et b
// Astuce : Utiliser &a, &b, et sizeof(double)
printf("%.2f\n", a);
printf("%.2f\n", b);
}
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète7Pointeurs sur fonctions
Déclaration de pointeurs sur fonctionsAppel de pointeurs sur fonctionsTypedef pour les pointeurs sur fonctionsPassage de fonctions en argumentsRécapitulatif : Dispatcher de calculatrice10Conteneurs génériques
Rappel sur les pointeurs voidWrapper génériqueÉchange génériqueComparaison génériqueRappel : tableaux génériques2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercle