Je souhaite stocker des types de données mixtes dans un tableau. Comment pourrait-on s'y prendre?
Réponses
Trop de publicités?Vous pouvez faire les éléments d'un tableau d'une discrimination de l'union, aka marqués de l'union.
struct {
enum { is_int, is_float, is_char } type;
union {
int ival;
float fval;
char cval;
} val;
} my_array[10];
L' type
membre est utilisé pour maintenir le choix des membres de l' union
doit être utilisée pour chaque élément du tableau. Donc, si vous voulez stocker un int
dans le premier élément, vous pouvez utiliser:
my_array[0].type = is_int;
my_array[0].val.ival = 3;
Lorsque vous souhaitez accéder à un élément du tableau, vous devez d'abord vérifier le type, puis utilisez le membre correspondant de l'union. Un switch
instruction est utile:
switch (my_array[n].type) {
case is_int:
// Do stuff for integer, using my_array[n].ival
break;
case is_float:
// Do stuff for float, using my_array[n].fval
break;
case is_char:
// Do stuff for char, using my_array[n].cvar
break;
default:
// Report an error, this shouldn't happen
}
Il est laissé au programmeur de s'assurer que l' type
membre correspond toujours à la dernière valeur stockée dans l' union
.
Les éléments de tableau doivent avoir la même taille, c’est pourquoi il n’est pas possible. Vous pourriez travailler autour d’elle en créant un type variant:
La taille de l’élément de l’union est la taille de l’élément le plus important, 4.
Il y a un style différent de la définition de la balise-union (quel que soit le nom) que l'OMI le rendre beaucoup plus agréable à l'utilisation, par la suppression de l'intérieur de l'union. C'est le style utilisé dans le Système X Window pour des choses comme des Événements.
L'exemple de la Barmar réponse donne le nom val
à l'intérieur de l'union. L'exemple de la Sp.'s réponse utilise un anonyme de l'union pour éviter d'avoir à spécifier l' .val.
chaque fois que vous accédez à la variante de l'enregistrement. Malheureusement, "anonyme" interne des structures et des syndicats n'est pas disponible en C89 ou C99. C'est un compilateur extension, et donc de nature non-portable.
Une meilleure façon de l'OMI est d'inverser l'ensemble de définition. Faire de chaque type de données de sa propre structure, et de mettre la balise (spécificateur de type) dans chaque structure.
typedef struct {
int tag;
int val;
} integer;
typedef struct {
int tag;
float val;
} real;
Puis vous enroulez-les dans un haut-niveau de l'union.
typedef union {
int tag;
integer int_;
real real_;
} record;
enum types { INVALID, INT, REAL };
Maintenant, il peut sembler que nous sommes de nous répéter, et nous sommes. Mais considérer que cette définition est susceptible d'être isolé dans un seul fichier. Mais nous avons éliminé le bruit de spécifier l'intermédiaire .val.
avant de vous obtenir les données.
record i;
i.tag = INT;
i.int_.val = 12;
record r;
r.tag = REAL;
r.real_.val = 57.0;
Au lieu de cela, il va à la fin, où il est moins désagréable. :D
Une autre chose que ce permet est une forme d'héritage. Edit: cette partie n'est pas en C standard, mais utilise une extension GNU.
if (r.tag == INT) {
integer x = r;
x.val = 36;
} else if (r.tag == REAL) {
real x = r;
x.val = 25.0;
}
integer g = { INT, 100 };
record rg = g;
Jusqu'-casting et vers le bas de la coulée.
Edit: Un piège pour savoir si vous êtes à la construction de l'un de ces avec C99 désigné initialiseurs. Tous les membres des initialiseurs devrait être par le même membre de l'union.
record problem = { .tag = INT, .int_.val = 3 };
problem.tag; // may not be initialized
L' .tag
initialiseur peut être ignoré par un compilateur optimisant, parce que l' .int_
initialiseur qui suit alias la même zone de données. Même si nous savons la mise en page (!), et il devrait être ok. Non, il n'est pas. Utiliser la fonction "interne" de la balise de la place (il se superpose à la balise extérieure, tout comme nous voulons, mais ne confondez pas le compilateur).
record not_a_problem = { .int_.tag = INT, .int_.val = 3 };
problem.tag; // == INT
Vous pouvez faire un tableau, avec un tableau séparé de
mais vous perdez le type d’informations.
Si vous avez besoin de garder le type d’information en quelque sorte garder un troisième tableau d’int (où l’int est une valeur énumérée) puis la fonction qui effectue un cast selon le code la `` valeur.