Saisie dynamique C

Préambule

Cet article a Ă©tĂ© Ă©crit et publiĂ© par moi sur mon site il y a plus de dix ans, le site lui-mĂȘme est depuis tombĂ© dans l'oubli, et je n'ai jamais commencĂ© Ă  Ă©crire quelque chose de plus intelligible en termes d'articles. Tout ce qui est dĂ©crit ci-dessous est le rĂ©sultat d'une Ă©tude du langage C par un gars de vingt ans, et, par consĂ©quent, ne prĂ©tend pas ĂȘtre un manuel, malgrĂ© le style de prĂ©sentation. Cependant, j'espĂšre sincĂšrement que cela encouragera les jeunes dĂ©veloppeurs Ă  se lancer dans l'expĂ©rimentation du C comme je l'ai fait autrefois.





Un avertissement

Ce court article s'avérera totalement inutile pour les programmeurs C/C++ expérimentés , mais pour certains débutants, il pourra faire gagner du temps. Je tiens à souligner que la plupart des bons livres sur C/C++ couvrent ce sujet dans une mesure suffisante.





Typage dynamique ou statique

De nombreux langages interprĂ©tĂ©s utilisent le typage dynamique. Cette approche permet de stocker des valeurs de diffĂ©rents types dans une variable de mĂȘme nom. Le langage C utilise un typage fort, ce qui, Ă  mon avis, est plus que correct. Cependant, il y a des moments (mais pas si souvent) oĂč il serait beaucoup plus pratique d'utiliser le typage dynamique. Souvent, un tel besoin est directement liĂ© Ă  une mauvaise conception, mais pas toujours. Ce n'est pas pour rien que Qt a un type QVariant



.





Ici, nous parlerons du langage C, bien que tout ce qui est décrit ci-dessous s'applique également au C++ .





Magie du pointeur du Vide

En fait, il n'y a pas de typage dynamique en C, et il ne peut pas y en avoir, mais il existe un pointeur universel dont le type est void *



. DĂ©clarer une variable de ce type, par exemple, comme argument d'une fonction, vous permet de lui passer un pointeur vers une variable de n'importe quel type, ce qui peut ĂȘtre extrĂȘmement utile. Et voici le premier exemple :





#include <stdio.h>

int main()
{
	void *var;
	int i = 22;
	var = &i;
	int *i_ptr = (int *)(var);

	if(i_ptr)
		printf("i_ptr: %d\n", *i_ptr);

	double d = 22.5;
	var = &d;
	double *d_ptr = (double *)(var);

	if(d_ptr)
		printf("d_ptr: %f\n", *d_ptr);

	return 0;
}
      
      



Production:





i_ptr: 22
d_ptr: 22.500000
      
      



Ici, nous avons attribuĂ© des pointeurs au mĂȘme pointeur (dĂ©solĂ© pour la tautologie) Ă  la fois au type int



et au double



.





: , void *



. , — , GCC . , , :





void *var;
int i = 22;
var = (void *)(&i);
      
      



.





. :





#include <stdio.h>

int lilround(const void *arg, const char type)
{
	if(type == 0) //   int
		return *((int *)arg); //     
	//   double

	double a = *((double *)arg);
	int b = (int)a;

	return b == (int)(a - 0.5) //    >= 0.5
		? b + 1 //   
		: b; //   
}

int main()
{
	int i = 12;
	double j = 12.5;

	printf("round int: %d\n", lilround(&i, 0)); //    
	printf("round double: %d\n", lilround(&j, 1)); //     

	return 0;
}
      
      



:





round int: 12
round double: 13
      
      



, , ( , ), . , - , .





, — lilround()



:





int lilround(const void *arg, const char type)
{
	return type == 0
		? *((int *)arg)
		: ((int)*((double *)arg) == (int)(*((double *)arg) - 0.5)
			? (int)(*((double *)arg)) + 1
			: (int)(*((double *)arg)));
}
      
      



, — — . 0



, int



, — double



. , , , - , .





, (struct



), . , . .





? : . , , ? : type



, , , . , , . . :





typedef struct {
	char type;
	int value;
} iStruct;

typedef struct {
	char type;
	double value;
} dStruct;
      
      



. :





typedef struct {
	char type;
	int value;
} iStruct;

typedef struct {
	double value;
	char type;
} dStruct;
      
      



, , , — , double value , , .





:





#include <stdio.h>

#pragma pack(push, 1)
typedef struct {
	char type; //   
	int value; //  
} iStruct;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct {
	char type; //   
	double value; //   
} dStruct;
#pragma pack(pop)

int lilround(const void *arg)
{
	iStruct *s = (iStruct *)arg;
	if(s->type == 0) //   int
		return s->value; //     
	//   double
	double a = ((dStruct *)arg)->value;
	int b = (int)a;
	return b == (int)(a - 0.5) //    >= 0.5
		? b + 1 //   
		: b; //   
}

int main()
{
	iStruct i;
	i.type = 0;
	i.value = 12;

	dStruct j;
	j.type = 1;
	j.value = 12.5;

	printf("round int: %d\n", lilround(&i)); //    
	printf("round double: %d\n", lilround(&j)); //     
	return 0;
}
      
      



: #pragma pack(push, 1)



#pragma pack(pop)



, . , . .





iStruct



type. , .





, , void-. , , , .. void



, C++ . , :





#include <stdio.h>

int main()
{
	int i = 22;
	void *var = &i; //  void-      i
	(*(int *)var)++; //  void-  int-,      

	printf("result: %d\n", i); //    i

	return 0;
}
      
      



: (*(int *)var)



.





C

. "" , , , . , type



:





typedef struct {
	void (*printType)(); //   ,  
	int (*round)(const void *); //   ,  
} uMethods;
      
      



Décrivons l'implémentation de ces fonctions pour différentes structures, ainsi que les fonctions d'initialisation pour différents types de structures. Le résultat est ci-dessous :





#include <stdio.h>

typedef struct {
	void (*printType)(); //   ,  
	int (*round)(const void *); //   ,  
} uMethods;

#pragma pack(push, 1)
typedef struct {
	uMethods m; //     
	int value; //  
} iStruct;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct {
	uMethods m; //     
	double value; //   
} dStruct;
#pragma pack(pop)

void intPrintType() //    iStruct
{
	printf("integer\n");
}

int intRound(const void *arg) //   iStruct
{
	return ((iStruct *)arg)->value; //      iStruct   
}

void intInit(iStruct *s) //  iStruct
{
	s->m.printType = intPrintType; //   printType      iStruct
	s->m.round = intRound; //   round      iStruct
	s->value = 0;
}

void doublePrintType() //    dStruct
{
	printf("double\n");
}

int doubleRound(const void *arg) //   dStruct
{
	double a = ((dStruct *)arg)->value;
	int b = (int)a;

	return b == (int)(a - 0.5) //    >= 0.5
			? b + 1 //   
			: b; //   
}

void doubleInit(dStruct *s)
{
	s->m.printType = doublePrintType; //   printType      dStruct
	s->m.round = doubleRound; //   round      dStruct
	s->value = 0;
}

int lilround(const void *arg)
{
	((iStruct *)arg)->m.printType(); //    ,    iStruct,   
	return ((iStruct *)arg)->m.round(arg); //   
}

int main()
{
	iStruct i;
	intInit(&i); //   
	i.value = 12;

	dStruct j;
	doubleInit(&j); //      
	j.value = 12.5;

	printf("round int: %d\n", lilround(&i)); //    
	printf("round double: %d\n", lilround(&j)); //     

	return 0;
}
      
      



Production:





integer
round int: 12
double
round double: 13
      
      



Remarque : seules les structures qui doivent ĂȘtre utilisĂ©es comme argument pour un pointeur void doivent ĂȘtre entourĂ©es de directives de compilateur.





Conclusion

Dans le dernier exemple, vous pouvez voir des similitudes avec l'OPP, ce qui, en gĂ©nĂ©ral, est vrai. Ici, nous crĂ©ons une structure, l'initialisons, la dĂ©finissons sur les champs de valeur clĂ© et appelons la fonction d'arrondi, qui, soit dit en passant, est extrĂȘmement simplifiĂ©e, bien que nous ayons ajoutĂ© ici l'infĂ©rence de type d'argument. C'est tout. Et rappelez-vous que vous devez utiliser ces constructions Ă  bon escient, car dans l'Ă©crasante majoritĂ© des tĂąches, leur prĂ©sence n'est pas requise.








All Articles