2016-08-18 1 views
1

J'ai un tampon charbuf contenant buf[0] = 10, buf[1] = 3, buf[2] = 3, buf[3] = 0, buf[4] = 58,Convertir tampon char struct

et une structure:

typedef struct 
{ 
    char type; 
    int version; 
    int length; 
}Header; 

Je voulais convertir le buf en Header. Maintenant, je suis en utilisant la fonction

int getByte(unsigned char* buf) 
{ 
    int number = buf[0]; 
    return number; 
} 

int getInt(unsigned char* buf) 
{ 
    int number = (buf[0]<<8)+buf[1]; 
    return number; 
} 

main() 
{ 
    Header *head = new Header; 
    int location = 0; 

    head->type = getByte(&buf[location]); 
    location++;  // location = 1 

    head->version = getInt(&buf[location]); 
    location += 2; // location = 3 

    head->ength = getInt(&buf[location]); 
    location += 2; // location = 5 
} 

Je suis à la recherche d'une solution telle que

Header *head = new Header; 

memcpy(head, buf, sizeof(head)); 

Dans cette, première valeur de la Header, head->type est propre et le repos est des ordures. Est-il possible de convertir unsigned char* buf en Header?

+3

Sizeof un pointeur est 4 ou 8. Vous voulez Sizeof * tête – stark

+0

depuis int est de 2 octets et caractère est 1 octet de taille de la tête doit être 5. Je vais le confirmer. –

+0

votre taille de caractères [5] est 5 octets votre en-tête est de 9 bytes Header.length ne sera jamais initialisé –

Répondre

4

Le seul portable complète et sécurisée est:

void convertToHeader(unsigned char const * const buffer, Header *header) 
{ 
    header->type = buffer[0]; 
    header->version = (buffer[1] << 8) | buffer[2]; 
    header->length = (buffer[3] << 8) | buffer[4]; 
} 

et

void convertFromHeader(Header const * const header, unsigned char * buffer) 
{ 
    buffer[0] = header->type; 
    buffer[1] = (static_cast<unsigned int>(header->version) >> 8) & 0xFF; 
    buffer[2] = header->version & 0xFF; 
    buffer[3] = (static_cast<unsigned int>(header->length) >> 8) & 0xFF; 
    buffer[4] = header->length & 0xFF; 
} 

Example

voir Converting bytes array to integer des explications

EDIT

Un résumé rapide du lien précédent: d'autres solutions possibles (memcpy ou union par exemple) ne sont pas portables selon l'endianess des différents systèmes (faire ce que vous faites est probablement pour une sorte de communication entre au moins deux systèmes hétérogènes) => certains des octets systèmes [0] est LSB de int et octet [1] est MSB et sur l'autre est l'inverse.

En outre, en raison de alignement, struct Header peut être plus grand que 5 octets (probablement 6 octets dans votre cas, si alignement est de 2 octets!) (Voir here par exemple)

Enfin, selon les restrictions d'alignement et les règles d'aliasing Sur certaines plateformes, le compilateur peut générer du code incorrect.

+0

en utilisant compilateur VS, sizeof (en-tête) est de 12 octets, en raison de la règle d'alignement dans Windows. –

+1

@veryhit Bien sûr, voir l'exemple d'alignement (linux avec gcc => 12 octets aussi). Je modifie pour clarifier ma réponse, parce que je prends juste le cas de l'utilisateur, ce qui semble être sur une plate-forme 16bits – Garf365

+0

Pourquoi tous les cas explicites (et malheureusement C-style) à 'std :: uint16_t'? Premièrement, ce type n'est même pas garanti, et je ne sais pas pourquoi vous n'avez pas simplement utilisé 'int' ... et de toute façon, les opérateurs shift /' & 'n'effectuent aucun cast requis vers' int '? (qui peut contenir les mêmes valeurs que 'uint16_t' s'il existe _did_) –

1

Ce que vous voulez aurait besoin de votre version et length pour avoir la même longueur que 2 éléments de votre tableau buf; c'est-à-dire que vous devez utiliser le type uint16_t, défini dans <cstdint>, plutôt que int, ce qui est probablement plus long. Et aussi vous devez faire buf un tableau de uint8_t, car char est autorisé à prendre plus de 1 octet! Vous devez également déplacer type jusqu'à la fin; sinon le compilateur insèrera presque certainement un octet de remplissage après lui pour pouvoir aligner version sur une limite de 2 octets (une fois que vous l'avez fait uint16_t et donc 2 octets); et alors votre buf[1] finirait là-bas plutôt que si vous le vouliez. Ceci est probablement ce que vous observez en ce moment, par la manière: en ayant un char suivi d'un int, ce qui est probablement 4 octets, vous avez 3 octets de rembourrage, et les éléments 1 à 3 de votre tableau sont là insérés (= perdu pour toujours).

Une autre solution consisterait à modifier votre tableau buf pour qu'il soit plus long et avoir également des octets de remplissage vides, afin que les données soient réellement alignées avec les champs struct. Il est à noter que, comme indiqué dans les commentaires, sizeof(head) renvoie la taille des pointeurs sur votre système, pas de la structure Header. Vous pouvez directement écrire sizeof(Header); mais à ce niveau de microgestion, vous ne perdrez plus de flexibilité si vous écrivez simplement "5", vraiment.

En outre, l'endianness peut visser avec vous. Les processeurs n'ont pas d'obligation de stocker les octets d'un nombre dans l'ordre que vous attendez plutôt que l'opposé; tous deux ont un sens interne après tout. Cela signifie que la copie en aveugle octets buf[0], buf[1] dans un nombre peut entraîner (buf[0]<<8)+buf[1], mais aussi dans (buf[1]<<8)+buf[0], ou même dans (buf[1]<<24)+(buf[0]<<16) si le type de données est 4 octets (comme int est généralement). Et même si cela fonctionne sur votre ordinateur maintenant, il y en a au moins un là où le même code entraînera des déchets. A moins que ces octets ne proviennent en réalité d'une réinterprétation d'un nombre en premier lieu. Dans ce cas, le code est incorrect (non portable) maintenant, cependant.

... vaut-il la peine? Tout compte fait, mon conseil est fortement de garder la façon dont vous les manipulez maintenant. Peut-être le simplifier.

Cela n'a vraiment aucun sens de convertir un octet en un int puis de nouveau en byte, ou de prendre l'adresse d'un octet pour le déréférencer à nouveau, ni besoin de variables auxiliaires sans nom descriptif et sans autre but que renvoyé, ou d'une variable dont vous connaissez la valeur à l'avance en tout temps.

Il suffit de faire

int getTwoBytes(unsigned char* buf) 
{ 
    return (buf[0]<<8)+buf[1]; 
} 

main() 
{ 
    Header *head = new Header; 

    head->type = buf[0]; 

    head->version = getTwoBytes(buf + 1); 

    head->length = getTwoBytes(buf + 3); 
} 
+1

"_Ce que vous voulez aurait besoin de votre version et la longueur d'avoir la même longueur que 2' char's, c'est si vous avez utilisé le type 'uint16_t'_" Ceci est un faux et Hypothèse dangereusement non portable. 'uint16_t' est seulement requis pour avoir exactement 16 bits de large; il n'a aucune relation avec la largeur de 'char'. Par exemple, sur une plateforme où 'char' est exactement de 16 bits (oui, ils existent), alors' sizeof (char) == sizeof (std :: uint16_t) == 1', ce qui contredit clairement ce que vous avez dit. –

+0

Il semblait que le moindre des soucis dans tout cela soit que la taille de char puisse être différente, mais oui, vous avez bien sûr raison. Je vais mettre à jour pour couvrir cela. –

-1

la meilleure façon est de créer une sorte de routines sérialisation/désérialisation de.

aussi, j'utiliser pas seulement int ou char types, mais utiliserais int32_t plus spécifique, etc., il est ainsi que la plate-forme indépendante (bien, en fait, vous pouvez également emballer vos structures de données avec pack pragma).

struct Header 
    { 
     char16_t type; 
     int32_t version; 
     int32_t length; 
    }; 
    struct Tools 
    { 
     std::shared_ptr<Header> deserializeHeader(const std::vector<unsigned char> &loadedBuffer) 
     { 
      std::shared_ptr<Header> header(new Header); 
      memcpy(&(*header), &loadedBuffer[0], sizeof(Header)); 
      return header; 
     } 
     std::vector<unsigned char> serializeHeader(const Header &header) 
     { 
      std::vector<unsigned char> buffer; 
      buffer.resize(sizeof(Header)); 
      memcpy(&buffer[0], &header, sizeof(Header)); 
      return buffer; 
     } 
    } 
    tools; 
    Header header = {'B', 5834, 4665}; 
    auto v1 = tools.serializeHeader(header); 
    auto v2 = tools.deserializeHeader(v1); 
+0

n'utilisez pas 'memcpy' pour sérialiser ou désérialiser une donnée, c'est dangereux: problème d'alignement (' sizeof (Header)! = 5' voir [ici] (http://coliru.stacked-crooked.com/a/7cce3c50ac756044)), non portable (endianess), et non sécurisé (voir restrictions d'alignement et règles d'alias) – Garf365

+0

@ Garf365 c'est pourquoi j'ai indiqué sur 'pragma pack' – fgrdn

+0

' pack pragma' peut résoudre les problèmes d'alignement, mais pas d'autres problèmes. De même, lors du changement d'alignement avec pragma pack, n'oubliez pas de le restaurer juste après. Et enfin, vous ajoutez quelques problèmes de portabilité parce que vous ne pouvez pas être sûr que le système cible supporte l'accès non aligné – Garf365