150 votes

Comment un type de données mixte (int, float, char, etc.) peut-il être stocké dans un tableau?

Je souhaite stocker des types de données mixtes dans un tableau. Comment pourrait-on s'y prendre?

249voto

Barmar Points 135986

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.

21voto

BSH Points 6645

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.

8voto

luser droog Points 9030

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

5voto

dzada Points 1487

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X