Конструкции переменного размера с прицепами - PullRequest
1 голос
/ 11 августа 2009

Я писал в этой теме ранее , но теперь у меня есть более конкретный вопрос / проблема.

Вот мой код:

#include <cstdlib>
#include <iostream>

typedef struct{
    unsigned int h;
    unsigned int b[];
    unsigned int t;
 } pkt;

 int main(){

    unsigned int* arr = (unsigned int*) malloc(sizeof(int) * 10);
    arr[0] = 0xafbb0000;
    arr[1] = 0xafbb0001;
    arr[2] = 0xafbb0011;
    arr[3] = 0xafbb0111;
    arr[4] = 0xafbb1111;
    arr[5] = 0xafbc0000;
    arr[6] = 0xafbc0001;
    arr[7] = 0xafbc0011;
    arr[8] = 0xafbc0111;
    arr[9] = 0xafbc1111;

    pkt* p = (pkt*) malloc(sizeof(int)*13);
    p->h = 0x0905006a;

    int counter;

Вот что я получаю для (счетчик = 0; счетчик <10; счетчик ++) p-> b [counter] = arr [counter];

    p->t = 0x55555555;

    std::cout << "header is \n" << p->h << std::endl;
    std::cout << "body is" << std::endl;
    for(counter=0; counter < 10;++counter)
            std::cout << std::hex << *((p->b)+counter) << std::endl;
    std::cout << "trailer is \n" << p->t << std::endl;

}

Вот что я получаю

header is 
151322730
body is
55555555
afbb0001
afbb0011
afbb0111
afbb1111
afbc0000
afbc0001
afbc0011
afbc0111
afbc1111
trailer is 
55555555

* (p-> b) заменяется на прицеп! И если я удаляю строку, где я назначил трейлер, то есть p-> t = 0x55555555 ;, то трейлер и p-> b совпадают (afbb0000).

Итак, мой вопрос: как я могу сохранить структуру, определяющую наверху, как она есть, и правильно настроить этот пакет.

EDIT:

Я должен быть более ясным. Я пишу это для сетевого приложения, поэтому мне нужно создать пакет, в котором выходные данные будут в точном порядке, я проверяю это, записывая в файл и выполняя шестнадцатеричный дамп. Так что мой вопрос на самом деле:

Как сделать так, чтобы пакет с заголовком, телом переменного размера и трейлером всегда имел этот порядок? Единственные два решения, о которых я могу думать, - это иметь много разных структур или вектор.

То есть я мог бы определить, где

typedef struct{
        unsigned int h;
        unsigned int b[12];
        unsigned int t;
     } pkt1;

typedef struct{
        unsigned int h;
        unsigned int b[102];
        unsigned int t;
     } pkt2;

etc 

или я мог бы сделать

std::vector<unsigned int> pkt (12);
pkt[0] = header;
pkt[1] = data;
...
pkt[2]= data;
pkt[11] = trailer;

Мне не очень нравится ни одно из этих решений. Есть ли лучший способ ??

Кроме того, это несколько отдельный вопрос, мне придется сделать то же самое для получения данных. Разумно сделать что-то вроде приведения блока данных к вектору? Я собираюсь получать данные как пустоту *, и я буду знать максимальную длину.

Ответы [ 6 ]

6 голосов
/ 11 августа 2009

Попробуйте обновить структуру и затем добавить следующее:

typedef struct{
    unsigned int h;
    unsigned int* b;
    unsigned int t;
 } pkt;

...
...

pkt p;
p->b = arr

Вот наглядный пример того, что он делает ...

|----------------------|
|     header           |
|----------------------|
|     B   int*         |  -------  p->b = arr
|----------------------|        | 
|     trailer          |        |
|----------------------|        v
                              |----------------------| 
                        arr   |                      |
                              |    B items [0 ... N] |
                              |                      |
                              |----------------------|

Если вы хотите быть хитрым, вы можете сделать что-то вроде этого ...

|----------------------|
|     header           |
|----------------------|
|     B   int*         |  -------  (= to memory address after trailer)
|----------------------|        | 
|     trailer          |        |
|----------------------|        |
|                      | <-------
|    B items [0 ... N] |
|                      |
|----------------------|

Вот рабочая версия позже:

#include "stdlib.h"
#include "stdio.h"
#include "malloc.h"

typedef struct{
    unsigned int h;
    unsigned int* b;
    unsigned int t;
 } pkt;

 int main(){

    pkt* p = (pkt*) malloc( sizeof(pkt) + sizeof(int)*10);
    p->h = 0x0905006a;
    p->b = &(p->t)+1;
    p->t = 0x55555555;

    p->b[0] = 0xafbb0000;
    p->b[1] = 0xafbb0001;
    p->b[2] = 0xafbb0011;
    p->b[3] = 0xafbb0111;
    p->b[4] = 0xafbb1111;
    p->b[5] = 0xafbc0000;
    p->b[6] = 0xafbc0001;
    p->b[7] = 0xafbc0011;
    p->b[8] = 0xafbc0111;
    p->b[9] = 0xafbc1111;

    int counter;
    printf( "header is \n" );
    printf( "%0x\n", p->h);
    printf( "body is\n" );
    for(counter=0; counter < 10;++counter)
            printf( "%0x\n", (p->b)[counter]);
    printf( "trailer is\n" );
    printf( "%0x\n", p->t );
 }
4 голосов
/ 11 августа 2009

В принципе, вы не можете. Я удивлен, что это даже компилируется. Вам разрешено иметь массив без указанного размера в качестве последнего элемента структуры из-за фундаментального способа работы структур в C / C ++.

Проблема в том, что компилятор должен знать, насколько велик b [], чтобы знать, где найти t в структуре. Обычно это делается путем просмотра размера массива. Поскольку вы не передали размер, ваш компилятор, очевидно, рассматривает его как ноль, что означает, что b [] и t фактически находятся в одном месте в памяти! Вам нужно либо указать размер b [] в объявлении структуры, либо передвинуть t перед b [].

2 голосов
/ 12 августа 2009

C ++ решение:

#include <iostream>
using namespace std;

class Packet
{
public:
  Packet (int body_size) :
    m_body_size (body_size)
  {
    m_data = new int [m_body_size + 2];
  }

  ~Packet ()
  {
    delete [] m_data;
    m_data = 0;
  }

  int &Header ()
  {
    return m_data [0];
  }

  int &Trailer ()
  {
    return m_data [m_body_size + 1];
  }

  int Size ()
  {
    return m_body_size;
  }

  int *Data ()
  {
    return m_data;
  }

  int &operator [] (int index)
  {
    return m_data [index + 1];
  }

  //  helper to write class data to an output stream
  friend ostream &operator << (ostream &out, Packet &packet)
  {
    out << "Header = " << packet.Header () << endl;
    out << "Data [" << packet.Size () << "]:" << endl;

    for (int i = 0 ; i < packet.Size () ; ++i)
    {
      out << "  [" << i << "] = 0x" << hex << packet [i] << endl;
    }

    out << "Trailer = " << packet.Trailer () << endl;

    return out;
  }

private:
  int
    m_body_size,
    *m_data;
};

//  simple test function for Packet class
int main ()
{
  Packet
    packet (10);

  packet.Header () = 0x0905006a;
  packet [0] = 0xafbb0000;
  packet [1] = 0xafbb0001;
  packet [2] = 0xafbb0011;
  packet [3] = 0xafbb0111;
  packet [4] = 0xafbb1111;
  packet [5] = 0xafbc0000;
  packet [6] = 0xafbc0001;
  packet [7] = 0xafbc0011;
  packet [8] = 0xafbc0111;
  packet [9] = 0xafbc1111;
  packet.Trailer () = 0x55555555;

  cout << packet;
}

Я не включил проверку ошибок или методы доступа const, вы можете ограничить проверку доступа к массиву. Получение данных просто:

Packet Read (stream in)
{
   get size of packet (exluding header/trailer)
   packet = new Packet (size)
   in.read (packet.Data, size + 2)
   return packet
}
1 голос
/ 11 августа 2009

Поскольку вы пометили вопрос C ++, вы всегда можете использовать вектор вместо int []. А еще лучше, сделайте pkt классом и скройте внутреннее представление от клиентов. Тогда у вас будет аксессор для заголовка и трейлера и индексатор для тела.

1 голос
/ 11 августа 2009

Когда вы объявляете unsigned int b [] в структуре, он не выделяет для нее места. Так что pkt.b и pkt.t - это одно и то же место, когда вы разыменовываете указатель структуры. Вы можете сделать b [] последним полем в структуре.

Однако, если вы хотите, чтобы t следовал b, вы можете попробовать этот пример C:

#include <stdio.h>
#include <malloc.h>

#define PKT_STRUCT(size) struct{unsigned int h;unsigned int b[size];unsigned int t;}

int main(void)
{

    PKT_STRUCT(10) *pkt  = malloc(sizeof(PKT_STRUCT(10)));

    pkt->h = 0x0905006a;

    pkt->b[0] = 0xafbb0000;
    pkt->b[1] = 0xafbb0001;
    pkt->b[2] = 0xafbb0011;
    pkt->b[3] = 0xafbb0111;
    pkt->b[4] = 0xafbb1111;
    pkt->b[5] = 0xafbc0000;
    pkt->b[6] = 0xafbc0001;
    pkt->b[7] = 0xafbc0011;
    pkt->b[8] = 0xafbc0111;
    pkt->b[9] = 0xafbc1111;

    pkt->t = 0x55555555;

    printf("Header:  %x\n", pkt->h);

    printf("Body:\n");
    int i;
    for(i=0; i<10; ++i)
        printf("%x\n", pkt->b[i]);

    printf("Tail:  %x\n", pkt->t);


    return 0;
}

Это может быть полезно, если вы работаете с данными, которые уже упакованы.

Также вы можете использовать объединение пакетов разного размера / структуры.

1 голос
/ 11 августа 2009

Если вы измените

typedef struct{
    unsigned int h;
    unsigned int b[];
    unsigned int t;
} pkt;

Для

typedef struct{
    unsigned int h;
    unsigned int *b;
    unsigned int t;
} pkt;

Я верю, что это сработает. Таким образом, b и t будут занимать свое собственное пространство, а не одно и то же. Вам также необходимо изменить malloc(sizeof(int) * 13); на malloc(sizeof(pkt)); - размер, который вы malloc() будете постоянным, и вам нужно malloc() дважды - один раз для struct и один раз для массива в b. Конечно, вы уже делаете это в любом случае, но таким образом вы не будете тратить кучу места.

Если вы хотите использовать b[] вместо *b (и для этого есть веские причины), вам нужно будет поставить его в конце. Сожалею. Вот как работают хаки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...