Malloc и memcpy struct plus array

Я пытаюсь найти лучшее название, поэтому прошу прощения, если это сбивает с толку.

У меня есть двоичное сообщение, которое я читаю через последовательный порт. Сообщение содержит постоянный 8-байтовый заголовок и динамическую длину сообщения (зависит от типа сообщения, определенного в 8-байтовом заголовке).

Я пытаюсь создать функцию, которая возвращает это сообщение с массивом, выделенным с помощью malloc.

Я изменил некоторые имена переменных / членов, чтобы сделать их более понятными. Пример:

#include <stdlib.h>
#include <stdio.h>

typedef struct MyMessageHeader {
  uint8_t byte1, byte2, byte3, byte4, byte5, byte6, byte7, 
} MyMessageHeader;

// I could have up to 100 different message types, but they all contain 
// the same header, so trying to make a message type with a header and array pointer
typedef struct MyMessage {
  MyMessageHeader header;
  uint8_t* data;
} MyMessage;

// Function to copy a raw byte array that is read from the serial port into
// a MyMessage object
MyMessage* FunctionThatIsNotWorking(uint8_t* rawBytes) {
  // Somehow we have determined in rawBytes that this message contains 12 bytes of data
  // plus the 8 bytes which is the header
  MyMessage* ret_msg = malloc(20);
  // This is where things go wrong and I get segmentation faults. Likely due to
  // my misunderstanding of malloc.
  // Copy the rawBytes into MyMessage. Assuming the first 8 bytes of rawBytes goes
  // into MyMessageHeader, and the last 12 bytes go into data
  memcpy(ret_msg, rawBytes, 20);
}

int main() {
uint8_t raw_bytes[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
MyMessage* my_msg = FunctionThatIsNotWorking(raw_bytes);
// Expecting now but this doesnt work
my_msg->header.byte1 == 1;
my_msg->header.byte2 == 2;
my_msg->header.byte3 == 3;
// ...
// first byte of data should be the 9th byte in raw_bytes 
my_msg->data[0] == 9;
my_msg->data[1] == 10;
}

Что мне здесь не хватает, или в чем мое неправильное понимание?

Всего 1 ответ


Я подозреваю, что то, что вам говорит (каламбур) - это структура отступов

ПРЕДЛОЖЕНИЕ:

  1. Читайте данные прямо с вашего устройства в локальный буфер. Сделайте буфер как минимум таким же длинным, как и наибольшее ожидаемое сообщение.

  2. Читайте шапку

  3. Выполните свой "malloc", и, наконец,

  4. Распакуйте данные из вашего буфера в вас


ОК: вот что я бы предложил:

/*
 * SAMPLE OUTPUT:
 * sizeof(*my_msg)= 16
 * header: 01 02 03 04 05 06 07 08
 * data: 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
 */
#include <stdio.h>
#include <stdint.h>   // unit8_t
#include <stdlib.h>   // malloc()
#include <string.h>   // memcpy ()

typedef struct MyMessage {
  uint8_t header[8];  // 8 bytes (you said) for header; message length unknown.
  uint8_t* data;      // Here, you're only allocating space for a pointer (e.g. 4 bytes)
} MyMessage_t;

MyMessage_t* FunctionThatIsNotWorking(uint8_t* rawBytes) {
  // Let's assume rawBytes contains header + data
  // Parse the header, determine message type, and determine message length
  // ... TBD ...

  // Allocate your struct
  MyMessage_t* ret_msg = malloc(sizeof (struct MyMessage));

  // Now allocate space for your *data* (let's assume this particular message has 12 bytes)
  ret_msg->data = malloc(12);

  // Copy your header (from rawBytes)
  memcpy(&ret_msg->header, rawBytes, 8);

  // Copy the data (starting on the ninth byte; assume the data is contiguous)
  memcpy(ret_msg->data, &rawBytes[8], 12);

  // Return the completed record
  return ret_msg;
}

int main() {
  int i;
  // Create some dummy data
  uint8_t raw_bytes[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

  MyMessage_t* my_msg = FunctionThatIsNotWorking(raw_bytes);

  printf ("sizeof(*my_msg)= %ld
", sizeof(*my_msg));
  printf ("header: ");
  for (i=0; i<sizeof(my_msg->header); i++) {
    printf("%02x ", my_msg->header[i]);
  }
  printf ("
data: ");
  for (i=0; i<12; i++) {
    printf("%02x ", my_msg->data[i]);
  }
  printf ("
");

  return 0;
}

Суть, которую я пытался сделать, заключается в том, что компоновщик байтов, который дает ваша структура, не обязательно соответствует ожиданиям. В полях часто есть «дополнительное расстояние», чтобы обеспечить выравнивание.

Если вы используете структуры на стороне отправки (при создании сообщения), компоновка данных в получателе не обязательно будет соответствовать компоновке в отправителе.

Следовательно, вам обычно нужно явно «упаковывать» и «распаковывать» сообщения, которые вы отправляете туда и обратно между разными хостами / разными платформами.

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

Наконец, в большинстве случаев длина сообщения будет неизвестна, пока вы действительно не прочитаете заголовок. Мой пример обрабатывает этот случай тоже.

Если вы хотите передать данные только вызывающей стороне (в моем примере копируются как заголовок, так и данные), вы, вероятно, захотите добавить поле «длина сообщения» в свою структуру.

Всегда старайтесь использовать оператор «sizeof» вместо жесткого кодирования «магических чисел». Следующая лучшая вещь - объявить константу (например, #define DATA_LENGTH 12 ).

'Надеюсь, это поможет!


Еще один пример.

Допустим, вы знаете, что данные вашего сообщения всегда будут ровно 12 байтов. и давайте все же предположим, что вы хотите, чтобы поле заголовка было 8 байтов. Ты можешь сделать это:

...
typedef struct MyMessage {
  uint8_t header[8]; // 8 bytes (you said) for header
  uint8_t data[12];  // This allocates 12 bytes
} MyMessage_t;
...
MyMessage_t* FunctionThatIsNotWorking(uint8_t* rawBytes) {
  // Let's assume rawBytes contains header + data
  // Parse the header, determine message type, and determine message length
  // ... TBD ...

  // Allocate your struct
  MyMessage_t* ret_msg = malloc(sizeof (*ret_msg));

  // Copy directly from rawBytes
  memcpy(ret_msg, rawBytes, sizeof (*ret_msg));

  // Return the completed record
  return ret_msg;
}

Есть идеи?

10000