sizeof style: sizeof (type) ou sizeof variable?

22

J'ai vu deux styles d'utilisation sizeofpour les opérations liées à la mémoire (comme dans memsetou malloc):

  • sizeof(type), et
  • sizeof variable ou sizeof(variable)

Lequel préféreriez-vous, ou utiliseriez-vous un mélange des deux styles, et quand utiliseriez-vous chaque style? Quels sont les avantages et les inconvénients de chaque style et quand les utilisez-vous?

À titre d'exemple, je peux voir la paire de situations suivante où un style aide et l'autre non:

Lorsque vous obtenez l'indirection du pointeur incorrecte:

type *var;
...
memset(var, 0, sizeof var);    /* oops */

Lorsque le type change:

new_type var;    /* changed from old_type to new_type */
...
memset(&var, 0, sizeof(old_type));    /* oops */
congusbongus
la source

Réponses:

30

Je perfer sizeof(variable)sur sizeof(type). Considérer:

int a1;
float a2;
memset(&a1,0,sizeof(a1));
memset(&a2,0,sizeof(a2));

contre.

int a1;
float a2;
memset(&a1,0,sizeof(int));
memset(&a2,0,sizeof(float));

Dans le premier cas, il est facile de vérifier que les bonnes tailles sont transmises memset. Dans le deuxième cas, vous devez constamment revoir les sections supérieure et inférieure pour vous assurer que vous êtes cohérent.

vy32
la source
4
De plus, si vous changez le type de la variable, le code qui suppose que le type devra changer. Je pense qu'il est préférable de corriger le code avec le moins de dépendance possible au type. (Je suis influencé par un grand projet de conversion de 16 bits à 32 bits dans la journée.)
Gort the Robot
C'est préférable pour travailler avec des tableaux C où il n'est pas courant de créer un typedef, mais utiliser des choses comme char array [MAX_SIZE];
mattnz
@ vy32: Mauvais 1: "sizeof (array) ne retourne PAS la taille du tableau ... taille du pointeur vers le tableau" - s'il arrays'agit bien d'un tableau C, puis sizeof(array)retourne le nombre d'octets requis pour stocker les éléments du tableau . Si arrayest un pointeur sur le premier élément d'un tableau C pourri, alors votre instruction est vraie. Passer un tableau C comme argument de fonction le fait ressembler à un pointeur dans la fonction - toutes les informations prouvant qu'il s'agissait d'un tableau, comme sa taille, sont perdues. Voilà pourquoi il est pourri . Faux 2: "les tableaux n'existent pas réellement en C; ... simplement du sucre syntaxique pour les pointeurs."
Johann Gerell
9

La préférence (comme toujours) est de refléter votre intention aussi directement que possible.

L'intention est-elle d'opérer contre la mémoire d'une variable existante? Si c'est le cas, alors utilisez sizeof(variable), car cela montre le plus fidèlement possible que c'est la mémoire de la variable elle-même qui vous intéresse.

L'intention est-elle d'effectuer un calcul sur le type, par exemple pour déterminer la quantité de mémoire à allouer pour une nouvelle instance? Si oui, utilisez sizeof(type).

Autrement dit, je préfère

struct foo *bar;
bar = (struct foo *) malloc(sizeof(struct foo));

plus de

bar = (struct foo *) malloc(sizeof(*bar));

comme ce dernier cas ressemble que vous essayez d'accéder à une variable qui n'existe pas, encore.

Par contre, je préfère

char Buffer[256];
memset(Buffer, 0, sizeof(Buffer));

plus de

char Buffer[256];
memset(Buffer, 0, 256 * sizeof(char));

comme l'intention est clairement de remplir à zéro le contenu de la variable, c'est donc la variable contre laquelle nous devons opérer. Essayer d'utiliser le type de métadonnées confond simplement les choses, ici.

Aidan Cully
la source
Je le fais toujours, mais c'est une question de goût personnel. Je considère que void *les fichiers sont automatiquement transtypés vers d'autres types de pointeurs comme une erreur.
Aidan Cully
3

Je préférerais largement sizeof(type)plus sizeof variable. Même si elle vous oblige à vous soucier plus sur le type et faire plus de changements, il vous aide à éviter les erreurs d'indirection de pointeur, qui se classent parmi les cause de bogues dans C .

Je ne m'inquiéterais pas autant des bugs dont le type sizeof(type)est changé; chaque fois que vous modifiez le type d'une variable, vous devez effectuer une analyse rapide pour voir où cette variable est utilisée et si vous devez modifier des sizeof(type)instructions. Même s'il y a toujours une chance de se tromper, cela présente un risque beaucoup plus faible que les erreurs d'indirection de pointeur.

Un avantage sizeof variableest pour les tableaux, mais dans la pratique, j'ai constaté que le passage de tableaux en paires de premier / len est beaucoup plus courant (dans ce cas, utilisez simplement la longueur), et il est également très facile de se tromper de taille en raison de désintégration tableau / pointeur, comme dans cet exemple:

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );    /* NOT the same as 3*3*sizeof(float) */
}
congusbongus
la source
2
"Analyse rapide pour voir où cette variable est utilisée ... il y a toujours une chance de se tromper" - pour moi, c'est le plus grand danger de faire sizeof (type), c'est pourquoi je fais toujours sizeof (variable). Parce que le résultat final de ce danger est que tout peut encore se compiler et même si vous faites une erreur, cela peut vous prendre plus d'un an ou des plantages aléatoires et des corruptions de tampon avant de l'attraper. Et je n'ai pas pu faire le lien (évidemment ma fenêtre de temps par question est d'environ 30 secondes) entre cette question et les "erreurs d'indirection du pointeur".
DXM
@DXM Avec l'opérande sizeof, si c'est une valeur alors c'est sizeof var; si c'est un pointeur c'est sizeof *var. C'est aussi quelque chose que les gens peuvent se tromper, et le compilateur le prendra volontiers sans se plaindre. Je soutiens que ces erreurs sont beaucoup plus difficiles à repérer et sont plus courantes que les erreurs de sizeof(type)forme.
congusbongus
1
Vos deux droits - l'opérateur C et la taille de l'opérateur sont fondamentalement cassés et ne seront jamais fiables, rien de ce que vous faites en tant que programmeur ne peut y remédier.
mattnz
Comme le post est balisé C, la publication de code C est plus informative que le code C ++ mat3_t::mat3_t(...).
chux
0

L'objectif est de supprimer la redondance. Si vous utilisez sizeof pour une opération liée à une variable, vous devez évidemment le refléter dans sizeof. Oui, vous pouvez gâcher comme dans le premier exemple, mais le remède n'est pas d'utiliser le type, mais * var, correspondant correctement au but.

Pour atténuer ces problèmes, il est habituel d'utiliser des macros, des modèles et des outils similaires formés pour le cas d'utilisation plutôt que sizeof nu.

Voir ma macro CLEAR qui est utilisée exclusivement au lieu d'un memset nu. J'ai sauvé mon cul plusieurs fois en cas de faute de frappe d'indirection ou lorsqu'un contenu de structure récupérait un vecteur ou une chaîne ...

Balog Pal
la source
Des macros comme celles-là sont à l'origine de la mauvaise réputation de la programmation de macros de C ...
mattnz
@mattnz: veuillez montrer la meilleure alternative. Il est beaucoup plus facile de parler de choses abstraites que de créer des programmes réellement opérationnels
Balog Pal
1
La macro liée est C ++, et ce post est étiqueté «C» (ce sont des langages différents), Ada serait ma suggestion.
mattnz
@mattnz: et le même sujet montre comment appliquer de manière similaire pour C. Vous semblez manquer complètement le point, ce n'est pas l'implémentation du contenu réel mais une utilisation plus sûre par rapport aux éléments bruts. Btw façons lisibles aussi.
Balog Pal
0

La logique métier définit votre choix.

  1. Si votre code fait référence à une variable particulière et sans cette variable, votre code n'a aucun sens - choisissez sizeof(var)

  2. Si vous codez traite d'un ensemble de variables avec un type particulier - choisissez sizeof(type). Habituellement, vous en avez besoin si vous en avez un typedefqui définit de nombreuses variables que vous traitez différemment selon leur type (par exemple la sérialisation). Vous ne savez peut-être pas laquelle de ces variables restera dans les futures versions de code, donc choisir le type comme argument est logiquement correct. Même le changement de ces typedefparamètres n'affectera pas votre taille de ligne.

Riga
la source
-1

Selon la taille de l'objectif (variable) pourrait être la meilleure solution. De cette façon, vous pouvez modifier votre type de variable si nécessaire, sans corriger toutes les entrées pour le type. Mais encore une fois, cela dépend de votre objectif.

Pedro Perez
la source