Mallo c и массив memcpy struct plus - PullRequest
       22

Mallo c и массив memcpy struct plus

1 голос
/ 10 февраля 2020

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

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

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

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

#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 Ответ

2 голосов
/ 10 февраля 2020

Я подозреваю, что вас окружает (каламбур): заполнение структуры

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

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

  2. Считайте заголовок

  3. Выполните ваш "mallo c" и наконец

  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\n", sizeof(*my_msg));
  printf ("header: ");
  for (i=0; i<sizeof(my_msg->header); i++) {
    printf("%02x ", my_msg->header[i]);
  }
  printf ("\ndata: ");
  for (i=0; i<12; i++) {
    printf("%02x ", my_msg->data[i]);
  }
  printf ("\n");

  return 0;
}

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

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

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

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

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

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

Всегда старайтесь использовать оператор "sizeof" вместо жесткого кодирования "magi c numbers". Следующая лучшая вещь - объявить константу (например, #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;
}
...