Pourquoi déclarer une structure qui ne contient qu'un tableau en C?

131

Je suis tombé sur un code contenant les éléments suivants:

struct ABC {
    unsigned long array[MAX];
} abc;

Quand est-il judicieux d'utiliser une déclaration comme celle-ci?

Joe.Z
la source

Réponses:

184

Il vous permet de passer le tableau à une fonction par valeur, ou de le renvoyer par valeur à partir d'une fonction.

Les structures peuvent être passées par valeur, contrairement aux tableaux qui se désintègrent en pointeur dans ces contextes.

Blagovest Buyukliev
la source
1
Méfiez-vous de faire cela avec des tableaux, plus de 16 ou 32 octets, pour les fonctions qui ne sont pas en ligne: il est plus efficace de les passer par référence const, à moins que l'appelé ait déjà besoin d'une copie tmp qu'il peut détruire. Si l'appel / le retour ne s'optimise pas, un tableau de taille moyenne à grande (des milliers d'octets) est une chose terrible à passer par valeur.
Peter Cordes
85

Un autre avantage est qu'il fait abstraction de la taille afin que vous n'ayez pas à utiliser [MAX]partout dans votre code partout où vous déclarez un tel objet. Cela pourrait également être réalisé avec

typedef char ABC[MAX];

mais alors vous avez un problème beaucoup plus gros: vous devez être conscient qu'il ABCs'agit d'un type de tableau (même si vous ne pouvez pas le voir lorsque vous déclarez des variables de type ABC) sinon vous serez piqué par le fait que ABCcela signifiera quelque chose de différent dans une liste d'arguments de fonction versus dans une déclaration / définition de variable.

Un autre avantage est que la structure vous permet d'ajouter ultérieurement plus d'éléments si vous en avez besoin, sans avoir à réécrire beaucoup de code.

R .. GitHub STOP AIDING ICE
la source
45

Vous pouvez copier une structure et renvoyer une structure à partir d'une fonction.

Vous ne pouvez pas faire cela avec un tableau - à moins qu'il ne fasse partie d'une structure!

Bo Persson
la source
26

Vous pouvez le copier comme ça.

struct ABC a, b;
........
a = b;

Pour un tableau, vous devez utiliser la fonction memcpy ou une boucle pour affecter chaque élément.

Métallique Prêtre
la source
6
(donc cela permet un code plus propre - cela ne fera aucune différence de vitesse, etc.)
John Carter
3
C'est utile. Malheureusement, vous ne pouvez pas faire si (a == b)!?! combien cela est incohérent. Pour C ++, il recherche un opérateur ==. En C, il est dit "opérandes invalides en binaire ==".
Matt du
11

Vous pouvez utiliser struct pour créer un nouveau type de données comme une chaîne . vous pouvez définir:

struct String {
    char Char[MAX];
};

ou vous pouvez créer une liste de données que vous pouvez utiliser par argument de fonctions ou la renvoyer dans vos méthodes. La structure est plus flexible qu'un tableau, car elle peut prendre en charge certains opérateurs comme = et vous pouvez y définir des méthodes.

J'espère que cela vous sera utile :)

Hossein Mobasher
la source
2
En gros, c'est la chose la plus proche de C pour créer une classe. J'aime cette réponse parce qu'elle se rapproche le plus de cela.
Nate CK
1
Aucune méthode en C. Les structures en C ne sont de simples données anciennes. Il a un opérateur = pris en charge par défaut (dont les autres réponses montrent que c'est la raison de le faire), mais c'est trompeur et s'applique principalement au C ++, pas à C.
Jonathan Sternberg
3
@J Sternberg: "Méthode" est juste une façon de considérer les sous-programmes comme étant liés aux "objets" de données qu'ils affectent. Vous pouvez certainement créer des «classes» d '«objets» et de «méthodes» qui opèrent sur eux en C. Le langage ne définit tout simplement pas formellement de telles choses. Si vous voulez créer de meilleures abstractions en C, insérer des éléments dans une structure est généralement la meilleure façon de le faire.
Nate CK
De plus, si vous vouliez vraiment "créer" des méthodes en C, vous pourriez utiliser des pointeurs de fonction (oui, oui, syntaxe délicate, pas de protection des données, etc.) pour associer des fonctions aux données sur lesquelles elles opèrent. Il faut passer "self" dans le premier argument (on pourrait même le nommer "this", si on voulait), puisqu'il n'y a pas de création automatique de ce pointeur à l'intérieur de la fonction en C. Bien sûr, c'est de la gymnastique, vous obtenez des choses comme celle-ci par défaut en C ++, bien qu'il soit vrai qu'il pourrait y avoir des frais généraux cachés en prime ...
BenPen
2

Un autre avantage de l'utilisation d'un tel structest qu'il applique la sécurité de type partout où un tel structest utilisé; surtout si vous avez deux types constitués de tableaux de même taille utilisés à des fins différentes, ces types vous aideront à éviter d'utiliser accidentellement un tableau de manière inappropriée.

Si vous n'enveloppez pas un tableau dans a struct, vous pouvez toujours déclarer a typedefpour lui: cela présente certains des avantages du struct- • le type est déclaré une fois, • la taille est automatiquement correcte, • l'intention du code devient plus claire, • et le code est plus facile à maintenir - mais vous perdez la ◦ sécurité de type stricte, ◦ la possibilité de copier et de renvoyer des valeurs de type et ◦ la possibilité d'ajouter des membres plus tard sans casser le reste de votre code. Deux typedefs pour des tableaux nus d'un type donné ne donnent des types différents que s'ils sont de tailles différentes. De plus, si vous utilisez le typedefsans *dans un argument de fonction, cela équivaut à char *une réduction drastique de la sécurité de type.

En résumé :

typedef struct A_s_s { char m[113]; } A_s_t; // Full type safey, assignable
typedef char   A_c_t[113];                   // Partial type-safety, not assignable

A_s_t          v_s(void);     // Allowed
A_c_t          v_c(void);     // Forbidden

void           s__v(A_s_t);     // Type-safe, pass by value
void           sP_v(A_s_t *);   // Type-safe
void           c__v(A_c_t);     // UNSAFE, just means char * (GRRR!)
void           cP_v(A_c_t *);   // SEMI-safe, accepts any array of 113
PJTraill
la source
1

Une structure peut contenir des fonctions d'initialisation de tableau, de copie et de finition émulant certains des avantages des paradigmes de gestion de la mémoire POO. En fait, il est très facile d'étendre ce concept pour écrire un utilitaire de gestion de mémoire générique (en utilisant la structure sizeof () pour savoir exactement combien d'octets sont gérés) pour gérer toute structure définie par l'utilisateur. De nombreuses bases de code de production intelligentes écrites en C les utilisent fortement et n'utilisent généralement jamais de tableau à moins que sa portée ne soit très locale.

En fait, pour un tableau incorporé dans une structure, vous pouvez faire d'autres «choses intelligentes» telles que la vérification des limites à chaque fois que vous souhaitez accéder à ce tableau. Encore une fois, à moins que la portée du tableau ne soit très limitée, c'est une mauvaise idée de l'utiliser et de transmettre des informations à travers les programmes. Tôt ou tard, vous rencontrerez des bugs qui vous empêcheront de dormir la nuit et ruineront vos week-ends.

Aniket
la source
Cela ne répond pas à la question de savoir pourquoi on pourrait utiliser un structcontenant uniquement un tableau.
PJTraill