Autres conseils

( Les réponses ci-dessus explique la raison très clairement, mais ne semble pas tout à fait clair sur la taille de rembourrage, donc, je vais ajouter une réponse selon ce que j'appris de The Lost Art d'emballage Structure , il a évolué de ne pas limiter à C, mais aussi applicable aux Go, Rust. )


Aligner la mémoire (pour struct)

Règles:

  • Avant chaque membre, il y aura un rembourrage de sorte que pour le faire démarrer à une adresse qui est divisible par sa taille.
    par exemple sur 64 bits système, int doit commencer à l'adresse divisible par 4, et par long 8, short par deux.
  • char et char[] sont spéciaux, pourrait être une adresse mémoire, ils ne ont pas besoin de rembourrage devant eux.
  • Pour struct, autre que le besoin d'alignement pour chaque membre, la taille de struct tout lui-même sera aligné sur une divisible de taille par la taille du plus grand membre individuel, par un rembourrage à la fin.
    par exemple si le plus grand élément de structure est alors long divisible par 8, int puis par 4, puis par short 2.

Ordre du membre:

  • L'ordre des membres pourrait affecter la taille réelle de struct, afin de prendre cela à l'esprit. et le stu_c par exemple stu_d de l'exemple ci-dessous ont les mêmes membres, mais dans un ordre différent, et le résultat en différentes tailles pour les 2 struct.

Adresse en mémoire (pour struct)

Règles:

  • système 64 bits Adresse de struct commence à partir des octets de (n * 16). ( Vous pouvez voir dans l'exemple ci-dessous, toutes les adresses hexagonales imprimées struct se terminent par 0. )
    Raison : le plus grand membre de struct individuel possible est de 16 octets (long double)
  • .
  • (Mise à jour) Si un struct ne contient que char en tant que membre, son adresse pourrait commencer à toute adresse.

Espace vide :

  • L'espace vide entre 2 struct pourrait être utilisé par des variables non-struct qui pourraient s'intégrer.
    par exemple dans test_struct_address() ci-après, réside variable x entre g et h struct adjacent.
    Peu importe si x est déclarée, l'adresse de h ne changera pas, x simplement réutilisé l'espace vide qui g gaspillée.
    Cas similaire pour y.

Exemple

( 64 système binaire )

memory_align.c :

/**
 * Memory align & padding - for struct.
 * compile: gcc memory_align.c
 * execute: ./a.out
 */ 
#include <stdio.h>

// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
    int i;
    char c;
};

// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
    long l;
    char c;
};

// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
    int i;
    long l;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
    long l;
    int i;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
    double d;
    int i;
    char c;
};

// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
    int i;
    double d;
    char c;
};

// size is 4,
struct stu_g {
    int i;
};

// size is 8,
struct stu_h {
    long l;
};

// test - padding within a single struct,
int test_struct_padding() {
    printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
    printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
    printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
    printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
    printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));

    return 0;
}

// test - address of struct,
int test_struct_address() {
    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    struct stu_g g;
    struct stu_h h;
    struct stu_f f1;
    struct stu_f f2;
    int x = 1;
    long y = 1;

    printf("address of %s: %p\n", "g", &g);
    printf("address of %s: %p\n", "h", &h);
    printf("address of %s: %p\n", "f1", &f1);
    printf("address of %s: %p\n", "f2", &f2);
    printf("address of %s: %p\n", "x", &x);
    printf("address of %s: %p\n", "y", &y);

    // g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));

    // h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));

    // f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));

    // x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
    printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
    printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));

    // y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
    printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
    printf("space between %s and %s: %ld\n", "h", "y", (long)(&y) - (long)(&h));

    return 0;
}

int main(int argc, char * argv[]) {
    test_struct_padding();
    // test_struct_address();

    return 0;
}

Résultat d'exécution - test_struct_padding() :

stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8

Résultat d'exécution - test_struct_address() :

stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0  // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0  // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc  // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8  // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between h and y: 8

début d'adresse Ainsi, pour chaque variable est g: d0 x: dc h: e0 y: E8

Je sais que cette question est ancienne et l'image la plupart des réponses ici explique très bien le rembourrage, mais en essayant de comprendre qu'il me je me suis dit avoir une « visuelle » de ce qui se passe aidé.

Le processeur lit la mémoire de « morceaux » d'une taille déterminée (mot). Dites le mot du processeur est de 8 octets. Il se penchera sur la mémoire comme une grande rangée de 8 octets des blocs de construction. Chaque fois qu'il a besoin d'obtenir des informations de la mémoire, il atteindra l'un de ces blocs et l'obtenir.

Variables d'alignement

Comme semblent dans l'image ci-dessus, peu importe où un Char (1 octet long) est, car il sera à l'intérieur d'un de ces blocs, ce qui nécessite la CPU pour traiter seulement 1 mot.

Lorsque nous traitons avec des données plus d'un octet, comme un 4 octets int ou double 8 octets, la façon dont ils sont alignés dans la mémoire fait une différence sur le nombre de mots devront être traitées par la CPU. Si des morceaux de 4 octets sont alignés de manière qu'ils correspondent toujours à l'intérieur d'un bloc (adresse de mémoire étant un multiple de 4), un seul mot aura à traiter. Sinon, un bloc de 4 octets peut avoir une partie de lui-même sur un bloc et sur une autre partie, ce qui nécessite le processeur pour traiter les 2 mots à lire ces données.

Le même applique à un double de 8 octets, sauf que maintenant il doit être un multiple d'adresse de mémoire de 8 pour garantir qu'il sera toujours dans un bloc.

considère comme un traitement de texte 8 octets, mais le concept s'applique à d'autres tailles de mots.

Le rembourrage fonctionne en comblant les lacunes entre ces données pour vous assurer qu'ils sont alignés avec ces blocs, améliorant ainsi les performances lors de la lecture de la mémoire.

Toutefois, comme indiqué sur les réponses des autres, parfois l'espace est plus important alors la performance elle-même. Peut-être que vous traitez beaucoup de données sur un ordinateur qui n'a pas beaucoup de RAM (espace d'échange pourrait être utilisé, mais il est beaucoup plus lent). Vous pouvez organiser les variables du programme jusqu'à ce que le moins le rembourrage est fait (comme il a été grandement illustré dans d'autres réponses), mais si cela ne suffit pas, vous pouvez explicitement désactiver le rembourrage, qui est ce que l'emballage est.

Le remplissage de la structure de garnissage de la structure, le rembourrage utilisé lorsque des questions d'alignement plus, l'emballage utilisé lorsque les questions spatiales plus.

Certains compilateurs fournissent #pragma pour supprimer le rembourrage ou pour le rendre emballé à un nombre n d'octets. Certains offrent des mots-clés pour ce faire. En général pragma qui est utilisée pour modifier la structure de rembourrage sera dans la forme ci-dessous (en fonction du compilateur):

#pragma pack(n)

Par exemple ARM fournit le mot-clé __packed pour supprimer le rembourrage de la structure. Allez dans votre manuel compilateur pour en savoir plus à ce sujet.

Donc, une structure est une structure tassée sans rembourrage.

En général, les structures emballées seront utilisées

  • pour économiser de l'espace

  • pour mettre en forme une structure de données à transmettre sur le réseau en utilisant certains protocole (ce n'est pas une bonne pratique bien sûr parce que vous devez
    traiter boutisme)

Rembourrage et l'emballage ne sont que deux aspects de la même chose:

  • emballage ou l'alignement est la taille à laquelle chaque élément est arrondi
  • rembourrage est l'espace supplémentaire ajouté pour correspondre à l'alignement

mystruct_A, en supposant un alignement par défaut de 4, chaque élément est aligné sur un multiple de 4 octets. Étant donné que la taille de char est 1, le rembourrage pour a et c est 4 - 1 = 3 octets tandis qu'aucun remplissage est requis pour int b qui est déjà 4 octets. Il fonctionne de la même façon pour mystruct_B.

emballage de la structure se fait que lorsque vous dites à votre compilateur explicitement pour emballer la structure. Rembourrage est ce que vous voyez. Le système 32 bits est rembourrage chaque champ pour l'alignement des mots. Si vous aviez dit à votre compilateur pour emballer les structures, ils seraient 6 et 5 octets, respectivement. Ne pas le faire bien. Il est un code non portable et fait compilateurs génèrent beaucoup plus lent (et parfois même poussette).

Il n'y a pas de mais à ce sujet! Qui veut saisir le sujet doit faire les suivants,

  
      
  • L'art perdu de l'emballage de la structure écrit par Eric S. Raymond
  •   
  • Coup d'œil sur l'exemple de code Eric
  •   
  • Last but not least, ne pas oublier la règle suivante sur le remplissage que struct est aligné sur l'alignement le plus grand type   exigences.
  •   

alignement de la structure de données est la manière dont les données est agencé et accéder à la mémoire de l'ordinateur. Il se compose de deux questions distinctes mais connexes: l'alignement des données et structure de données rembourrage . Quand un ordinateur moderne lit ou écrit à une adresse mémoire, il le fera en morceaux de taille mot (par exemple 4 morceaux d'octets sur un système 32 bits) ou plus. des moyens d'alignement de données mettant les données à une adresse de mémoire égale à un multiple de la taille de mot, ce qui augmente en raison de la façon dont les poignées de CPU de mémoire de la performance du système. Pour aligner les données, il peut être nécessaire d'insérer des octets vides de sens entre la fin de la dernière structure de données et le début de la prochaine, qui est le rembourrage de structure de données.

  1. Afin d'aligner les données dans la mémoire, un ou plusieurs octets vides (adresses) sont insérés (ou laissé vide) entre des adresses de mémoire qui sont attribuées à d'autres éléments de structure tout d'allocation de mémoire. Ce concept est appelé rembourrage de la structure.
  2. Architecture d'un processeur d'ordinateur est un moyen de telle sorte qu'il puisse lire 1 mot (4 octets dans le processeur de 32 bits) à partir de la mémoire à la fois.
  3. Pour utiliser cet avantage de processeur, les données sont toujours alignés en quatre octets de paquet qui conduit à insérer des adresses vides entre l'adresse d'un autre élément.
  4. En raison de ce concept de remplissage de la structure en C, la taille de la structure est pas toujours identique à ce que nous pensons.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top