Pergunta

Eu estou tentando lançar um fluxo de dados em um struct já que o fluxo de dados consiste em mensagens de largura fixa e cada mensagem tenha fulle definido fixo largura campos também. Eu estava pensando em criar uma estrutura e depois usar reinterpret_cast para ponteiro elenco para o fluxo de dados para a estrutura para obter os campos. Eu fiz um código de teste e obter resultados estranhos. Poderia qualquer explicar por que estou recebendo estes ou como corrigir o código. (O fluxo de dados será binário e alfa numérico misto, mas estou apenas testando com cordas)

#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;
}

O resultado que estão chegando são

4
four
our
ur
r

Eu acho que o quatro, o nosso e ur está imprimindo uma vez que não consigo encontrar o terminador nulo. Como faço para obter em torno da questão nulo terminador?

Foi útil?

Solução

A fim de ser capaz de imprimir uma matriz de caracteres, e ser capaz de distingui-lo de uma string terminada em null, você precisa de outras definições 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;
}

Outras dicas

Você está certo sobre a falta de terminador nulo. A razão é a impressão de "ur" novamente é porque você repetiu o cabeçalho são> terceira vez do cabeçalho são> quarta. Em vez de "char [1]", porque não basta declarar essas variáveis ??como "char"?

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

A questão não é reinterpret_cast (embora usá-lo é uma idéia muito ruim), mas nos tipos das coisas no struct. Eles devem ser do tipo 'char', não do tipo '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;
}

Na minha experiência, usando #pragma pack tem causado dores de cabeça - parcialmente devido a um compilador que não aparecer corretamente, mas também devido a desenvolvedores esquecendo de pop em um cabeçalho. Um erro como esse e estruturas acabam definido de forma diferente dependendo de qual fim cabeçalhos são incluídos em uma unidade de compilação. É um pesadelo de depuração.

Eu tento não fazer sobreposições de memória por essa razão - você não pode confiar que a sua estrutura é devidamente alinhados com os dados que você está esperando. Em vez disso, criar estruturas (ou classes) que contêm os dados de uma mensagem em um formato "nativo" C ++. Por exemplo, você não precisa de um campo "filler" definido se é apenas para fins de alinhamento. E, talvez, que faz mais sentido para o tipo de um campo para ser int do que para que seja char[4]. Logo que possível, traduzir o fluxo de dados para o tipo "nativo".

Assumindo que você quiser continuar usando um struct overlayable (que é sensível, uma vez que evita a cópia em código de Alexey), você pode substituir suas matrizes de caracteres matérias com um invólucro como o seguinte:

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;
}

Em seguida, seus estruturas geradas será algo como:

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

e seu código existente deve funcionar bem.

NB. você pode adicionar métodos para FixedStr se você quiser (por exemplo, std::string FixedStr::toString()) só não adicionar métodos virtuais ou herança, e ele irá sobrepor bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top