Zephyr – C – глубокая копия структуры дает неожиданный результат

Некоторый контекст

В настоящее время я работаю с Bluetooth Low Energy над встроенной системой с использованием Zephyr.

Во время обнаружения служб/характеристик/CCCD (мое встроенное устройство является центральным устройством BLE) я хочу сохранить все характеристики, чтобы иметь возможность использовать их позже (в основном читать их).

Для этого я использую связанный список , предоставленный Zephyr, и выделяю свой узел с помощью k_calloc().

typedef struct lst_chrc_node_s {
  sys_snode_t     node;
  struct bt_uuid  *uuid;
  uint16_t        value_handle;
  uint8_t         properties;
  uint8_t         type;
} lst_chrc_node_t;

lst_chrc_node_t *list_create_node(struct bt_gatt_chrc *chrc, uint8_t type)
{
  lst_chrc_node_t *new_node = k_calloc(1, sizeof(new_node));
  if (!new_node)
    return NULL;
  
  new_node->uuid = k_calloc(1, sizeof(new_node->uuid));
  if (!new_node->uuid)
  {
    k_free(new_node);
    return NULL;
  }
  
  print_uuid128("UUID that will be store", chrc->uuid);
  *new_node->uuid = *chrc->uuid;
  new_node->value_handle = chrc->value_handle;
  new_node->properties = chrc->properties;
  new_node->type = type;
  print_uuid128("UUID that was stored   ", new_node->uuid);

  return new_node;
}

Как видите, я хочу сохранить структуру bt_gatt_chrc *chrc, передаваемую в аргументах функции. Однако эта структура содержит постоянный указатель на другую структуру с именем bt_uuid. Эта структура bt_uuid важна, поскольку она понадобится мне для считывания правильных характеристик с периферийного устройства BLE.

Узел моего связанного списка не имеет непосредственно структуры bt_gatt_chrc, но имеет один и тот же элемент один за другим. Это потому, что я не могу напрямую скопировать bt_gatt_chrc из аргумента в узел связанного списка, потому что константный указатель на bt_uuid имеет тот же адрес при обнаружении служб, характеристик и CCCD (он меняется при обнаружении другого типа атрибута GATT, но для этого он останется тем же тип атрибута).

Проблема

После создания узла и копирования в него данных значение bt_uuid отличается от того, которое я получил от периферийного устройства BLE.

Вот журнал для одного стандартного UUID:

[00:00:04.293,914] <inf> main: UUID received           UUID 128: 070002a4-5720-0045-2020-0013072a0000
[00:00:04.293,945] <inf> main: UUID that will be store UUID 128: 070002a4-5720-0045-2020-0013072a0000
[00:00:04.293,975] <inf> main: UUID that was stored    UUID 128: 00000d00-0d61-8c00-0200-020003000000

Я чувствую себя довольно глупо, потому что, AFAIU, моя проблема явно связана с этой линией *new_node->uuid = *chrc->uuid;, но я не понимаю, что делаю неправильно. Я попытался преобразовать const struct bt_uuid просто в struct bt_uuid, результат тоже был не очень хорошим (это выглядит еще более неправильным, поскольку весь 128-битный UUID меняется между каждой стандартной характеристикой (определенной Bluetooth SIG) вместо того, чтобы оставаться довольно близким к то же самое (например, между характеристиками меняются всего 2-3 цифры без приведения структуры).

Это потому, что bt_uuid — это «предварительный» тип, и поэтому я не могу использовать его таким образом? Может ли кто-нибудь перефразировать, что такое предварительный тип? Я явно не уверен в своем понимании этого.

Я понятия не имею, что такое предварительный тип, но он выглядит как docs.zephyrproject.org/apidoc/latest/uuid_8h_source.html хранилище для uuid зависит от типа, и пространство выделяется динамически. Это один из дешевых способов реализации наследования в C.

KamilCuk 27.08.2024 17:28

Я думаю, вы имели в виду не «предварительный» тип, а «неполный». Но если бы тип был неполным, вы не смогли бы использовать к нему sizeof или =.

Ian Abbott 27.08.2024 17:29
k_calloc(1, sizeof(new_node)) выглядит крайне подозрительно. Разве вместо этого не должно быть sizeof *new_node? То же самое и с new_node->uuid = k_calloc(1, sizeof(new_node->uuid));.
ach 27.08.2024 17:56

@IanAbbott Я сказал «предварительно», потому что так говорится в документации Zephyr

Groux 28.08.2024 09:04

@KamilCuk О, я не знал об этой реализации наследования в C, это довольно интересно

Groux 28.08.2024 09:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Из uuid.h:

enum {
        BT_UUID_TYPE_16,
        BT_UUID_TYPE_32,
        BT_UUID_TYPE_128,
};
 
#define BT_UUID_SIZE_16                  2
 
#define BT_UUID_SIZE_32                  4
 
#define BT_UUID_SIZE_128                 16
 
struct bt_uuid {
        uint8_t type;
};
 
struct bt_uuid_16 {
        struct bt_uuid uuid;
        uint16_t val;
};
 
struct bt_uuid_32 {
        struct bt_uuid uuid;
        uint32_t val;
};
 
struct bt_uuid_128 {
        struct bt_uuid uuid;
        uint8_t val[BT_UUID_SIZE_128];
};

struct bt_uuid * должен указывать на достаточно памяти для хранения struct bt_uuid_16, struct bt_uuid_32 или struct bt_uuid_128, в зависимости от значения члена typeBT_UUID_TYPE_16, BT_UUID_TYPE_32 или BT_UUID_TYPE_128.

Обновлено: строка lst_chrc_node_t *new_node = k_calloc(1, sizeof(new_node)); в исходном коде OP неверна (замечена @chux в комментарии ниже и @ach в комментарии к вопросу), и исправлена ​​в код ниже.

Непроверенный, модифицированный код:

lst_chrc_node_t *list_create_node(struct bt_gatt_chrc *chrc, uint8_t type)
{
  lst_chrc_node_t *new_node;
  size_t bt_uuid_size;

  switch (chrc->uuid->type)
  {
  case BT_UUID_TYPE_16:
    bt_uuid_size = sizeof(struct bt_uuid_16);
    break;
  case BT_UUID_TYPE_32:
    bt_uuid_size = sizeof(struct bt_uuid_32);
    break;
  case BT_UUID_TYPE_128:
    bt_uuid_size = sizeof(struct bt_uuid_128);
    break;
  default:
    return NULL;
  }
  new_node = k_calloc(1, sizeof(*new_node));
  if (!new_node)
    return NULL;
  
  new_node->uuid = k_calloc(1, bt_uuid_size);
  if (!new_node->uuid)
  {
    k_free(new_node);
    return NULL;
  }
  
  print_uuid128("UUID that will be store", chrc->uuid);
  memcpy(new_node->uuid, chrc->uuid, bt_uuid_size);
  new_node->value_handle = chrc->value_handle;
  new_node->properties = chrc->properties;
  new_node->type = type;
  print_uuid128("UUID that was stored   ", new_node->uuid);

  return new_node;
}
new_node = k_calloc(1, sizeof(new_node)); выглядит неправильно. Вы хотели new_node = k_calloc(1, sizeof(new_node[0])); ?
chux - Reinstate Monica 27.08.2024 21:21

@chux Спасибо, я внес исправление.

Ian Abbott 28.08.2024 11:02

Другие вопросы по теме