Question

Je suis en train de jeter un flux de données dans un struct puisque le flux de données se compose de messages à largeur fixe et chaque message a Fulle défini des champs de largeur fixe ainsi. Je comptais sur la création d'un struct, puis en utilisant reinterpret_cast pour lancer pointeur sur le flux de données à la struct pour obtenir les champs. J'ai fait un code de test et obtenir des résultats étranges. Quelqu'un pourrait-expliquer pourquoi je reçois ce ou comment corriger le code. (Le flux de données sera binaire et mixte alpha numérique, mais im tester uniquement des cordes)

#pragma pack(push,1)
struct Header 
{
    char msgType[1];
    char filler[1];
    char third[1];
    char fourth[1];
};
#pragma pack(pop)

int main(void)
{
    cout << sizeof(Header) << endl;

    char* data = "four";
    Header* header = reinterpret_cast<Header*>(data);
    cout << header->msgType << endl;
    cout << header ->filler << endl;
    cout << header->third << endl;
    cout << header->fourth << endl;
    return 0;
}

Le résultat qui arrivent sont

4
four
our
ur
r

Je pense que les quatre, notre et ur est l'impression, car il ne peut pas trouver la terminaison nulle. Comment puis-je obtenir autour de la question de terminaison nulle?

Était-ce utile?

La solution

Pour être en mesure d'imprimer un tableau de caractères, et être capable de le distinguer d'une chaîne terminée par zéro, vous avez besoin d'autres définitions de operator<<:

template< size_t N >
std::ostream& operator<<( std::ostream& out, char (&array)[N] ) {
     for( size_t i = 0; i != N; ++i ) out << array[i];
     return out;
}

Autres conseils

Vous avez raison de l'absence de terminaison nulle. La raison pour laquelle il est l'impression « ur » est de nouveau parce que vous répétiez la tête-> troisième au lieu de tête-> quatrième. Au lieu de "char [1]", pourquoi ne pas déclarer que ces variables comme "char"?

struct Header 
{
    char msgType;
    char filler;
    char third;
    char fourth;
};

La question ne reinterpret_cast (bien que son utilisation est une très mauvaise idée), mais dans les types de choses dans le struct. Ils doivent être de type 'char', pas de type 'char [1].

#pragma pack(push,1)
template<int N>
struct THeader 
{
    char msgType[1+N];
    char filler[1+N];
    char third[1+N];
    char fourth[1+N];
};
typedef THeader<0> Header0;
typedef THeader<1> Header1;  
Header1 Convert(const Header0 & h0) {
   Header1  h1 = {0};
   std::copy(h0.msgType, h0.msgType + sizeof(h0.msgType)/sizeof(h0.msgType[0]), h1.msgType);
   std::copy(h0.filler, h0.filler+ sizeof(h0.filler)/sizeof(h0.filler[0]), h1.filler);
   std::copy(h0.third , h0.third + sizeof(h0.third) /sizeof(h0.third [0]), h1.third);
   std::copy(h0.fourth, h0.fourth+ sizeof(h0.fourth)/sizeof(h0.fourth[0]), h1.fourth);
   return h1;
}
#pragma pack(pop)


int main(void)
{
  cout << sizeof(Header) << endl;
  char* data = "four";
  Header0* header0 = reinterpret_cast<Header*>(data);
  Header1 header = Convert(*header0);
  cout << header.msgType << endl;
  cout << header.filler << endl;
  cout << header.third << endl;
  cout << header.fourth << endl;
  return 0;
}

Dans mon expérience, en utilisant #pragma pack a causé des maux de tête - en partie en raison d'un compilateur qui ne sort pas correctement, mais aussi en raison de développeurs oublier de la pop dans un en-tête. Une erreur comme ça et struct finissent différemment défini selon le pour en-têtes sont inclus dans une unité de compilation. Il est un cauchemar de débogage.

J'essaie de ne pas faire des superpositions de mémoire pour cette raison - vous ne pouvez pas faire confiance que votre struct est correctement aligné avec les données que vous attendez. Au lieu de cela, je crée struct (ou classes) qui contiennent les données d'un message dans un format « natif » C ++. Par exemple, vous n'avez pas besoin d'un champ « de remplissage » défini si elle est juste là à des fins d'alignement. Et peut-être il est plus logique pour le type d'un champ à int que pour être char[4]. Dès que possible, traduire le flux de données dans le type « natif ».

En supposant que vous voulez continuer à utiliser une struct superposables (qui est sensible, car elle évite la copie dans le code de Alexey), vous pouvez remplacer vos tableaux char premières avec une enveloppe comme suit:

template <int N> struct FixedStr {
    char v[N];
};

template <int N>
std::ostream& operator<<( std::ostream& out, FixedStr const &str) {
    char const *nul = (char const *)memchr(str.v, 0, N);
    int n = (nul == NULL) ? N : nul-str.v;
    out.write(str.v, n);
    return out;
}

Ensuite, vos structures générées ressemblera à ceci:

struct Header 
{
    FixedStr<1> msgType;
    FixedStr<1> filler;
    FixedStr<1> third;
    FixedStr<40> forty;
};

et votre code existant devrait fonctionner correctement.

NB. vous pouvez ajouter des méthodes à FixedStr si vous voulez (par exemple, std::string FixedStr::toString()) ne concordent pas des méthodes virtuelles ou l'héritage, et il se superposeront bien.

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