Question

Lors d'un examen de code, je suis venu à travers un code qui définit une structure simple comme suit:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
}

D'autre part, est définie une série de ces objets:

foo listOfFoos[SOME_NUM];

Par la suite, les structures sont copiées-brut dans un tampon:

memcpy(pBuff,listOfFoos,3*SOME_NUM);

Ce code repose sur les hypothèses suivantes:. A) La taille de foo est 3, et pas de remplissage est appliquée, et b) Un ensemble de ces objets est emballé sans rembourrage entre eux

..

Je l'ai essayé avec GNU sur deux plates-formes (RedHat 64b, Solaris 9), et cela a fonctionné sur les deux.

Les hypothèses ci-dessus valables? Sinon, dans quelles conditions (par exemple le changement dans OS / compilateur) pourraient-ils pas?

Était-ce utile?

La solution

Un tableau d'objets doit être contigus, donc il n'y a jamais padding entre les objets, bien que le rembourrage peut être ajouté à la fin d'un objet (produisant à peu près le même effet).

Étant donné que vous travaillez avec Char, les hypothèses sont probablement raison le plus souvent, mais la norme C ne garantit certainement pas. Un autre compilateur, ou même simplement un changement dans les drapeaux passés à votre compilateur actuel pourrait entraîner un rembourrage étant inséré entre les éléments de la structure ou suivant le dernier élément du struct, ou les deux.

Autres conseils

Il serait certainement plus sûr de le faire:

sizeof(foo) * SOME_NUM

Si vous copiez votre tableau comme cela, vous devez utiliser

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

Ce sera toujours travailler aussi longtemps que vous avez alloué pBuff à la même taille. De cette façon, vous faites aucune hypothèse sur le rembourrage et l'alignement du tout.

La plupart des compilateurs alignent une struct ou une classe à l'alignement requis pour le plus grand type inclus. Dans votre cas de caractères qui signifie pas d'alignement et le rembourrage, mais si vous ajoutez un court par exemple votre classe serait 6 octets grand avec un octet de remplissage ajouté entre le dernier caractère et votre court.

Je pense que la raison pour que cela fonctionne parce que tous les champs de la structure sont ombles qui alignent un. S'il y a au moins un champ qui ne correspond pas 1, l'alignement de la structure / classe ne sera pas 1 (l'alignement sera dépend de l'ordre et de l'alignement champ).

Voyons quelques exemples:

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    unsigned short i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

Lors de son exécution, le résultat est:

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

Vous pouvez voir que Bar et F_B a un alignement 2, de sorte que son champ i sera correctement aligné. Vous pouvez également voir que la taille de Bar est 6 et non 5 . De même, la taille de b_f (5 bar) 30 et non 25 .

Donc, si vous est un code dur au lieu de sizeof(...), vous obtiendrez un problème ici.

Hope this helps.

Le tout se résume à l'alignement de la mémoire. machines typiques 32 bits lecture ou d'écriture 4 octets de mémoire par tentative. Cette structure est à l'abri des problèmes parce qu'il relève que 4 octets facilement sans problème de remplissage confus.

Maintenant, si la structure était en tant que telle:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

Votre logique de collègues de travail conduirait probablement à

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 = 3 octets de char, 2 ints = 2 * 4 octets, si 3 + 8)

Malheureusement, en raison de rembourrage de la structure prend réellement 12 octets. En effet, vous ne pouvez pas tenir trois Char et un entier dans ce mot de 4 octets, et donc il y a un octet d'espace rembourré il qui pousse l'int dans son propre mot. Cela devient de plus en plus d'un problème les plus divers types de données deviennent.

Pour les situations où des choses comme cela est utilisé, et je ne peux pas l'éviter, j'essaie de faire la pause de compilation lorsque les présomptions ne tiennent plus. J'utilise quelque chose comme ce qui suit (ou Boost.StaticAssert si la situation le permet):

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}

J'aurais été sûr et remplacé le nombre magique 3 avec un sizeof(foo) je pense.

Je suppose que le code optimisé pour les architectures de futurs processeurs introduira probablement une certaine forme de rembourrage.

Et en essayant de traquer ce genre de bug est une vraie douleur!

Comme d'autres l'ont dit, en utilisant sizeof (foo) est un pari plus sûr. Certains compilateurs (en particulier ésotériques dans le monde de l'embarqué) ajoutera un en-tête de 4 octets à des classes. D'autres peuvent faire des tours-alignement de mémoire funky en fonction de vos paramètres du compilateur.

Pour une plate-forme grand public, vous êtes probablement bien, mais ce ne est pas une garantie.

Il pourrait y avoir encore un problème avec sizeof () lorsque vous passez les données entre deux ordinateurs. Sur l'un d'eux, le code peut compiler avec un rembourrage et l'autre sans, auquel cas sizeof () donnerait des résultats différents. Si les données de tableau est passé d'un ordinateur à l'autre, il sera mal interprété parce que les éléments du tableau ne seront pas trouvés où prévu. Une solution consiste à faire en sorte que #pragma paquet (1) est utilisé chaque fois que possible, mais cela peut ne pas être suffisant pour les tableaux. Le mieux est de prévoir le problème et d'utiliser un rembourrage à un multiple de 8 octets par élément de réseau.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top