Problema usando reinterpret_cast <> em c ++
-
18-09-2019 - |
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?
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.