【发布时间】:2021-04-24 18:50:21
【问题描述】:
最近我一直在使用 .FIT 文件格式和 garmin 提供的 SDK。简而言之,我需要发送与消息定义相对应的消息定义和消息数据。有许多类型的消息,几乎所有时间我都需要使用其中的几个 - 而 SDK 不提供,它发送所有受支持的消息。 我需要存储不同数据类型的变量并将它们与特定消息相关联。目前,变量存储在如下结构中:
//Example
typedef struct
{
uint32_t timestamp; // 1 * s + 0,
uint16_t speed; // 1000 * m/s + 0,
uint8_t heart_rate; // 1 * bpm + 0,
} FIT_RECORD_MESG;
但是,每个 FIT_(x)_MESG 的变量/数组数量以及数据类型都不同。这是 FIT_RECORD_MESG 的示例。
我已经创建了创建数组的函数,其中包含应该发送哪些变量/数组的信息。
void write_field(FIT_UINT8 field)
{
fields_mesg.fields_number++; //number of messages
fields_mesg.fields_array = (uint8_t*) realloc(fields_mesg.fields_array, sizeof(uint8_t)*fields_mesg.fields_number); //append message type
fields_mesg.fields_array[fields_mesg.fields_number-1] = field; //array of gloabl field types
}
/*
Example array:
[0,2];
0 - timestamp
2 - heart_rate
variable speed should not be sent
*/
每个结构可以包含不同数量的变量/数组以及数据类型。如果结构中的所有数据类型都是相同的类型(并且没有数组),我可以创建一个指向数据结构的指针和在 write_field() 中创建的 fields_mesg.fields_array将是结构指针的偏移量。不幸的是,在我的情况下无法做到,因为数据类型可能会有所不同,并且结构可以包含数组。我正在考虑创建 3 个相应数据类型的数组/结构,但它会更复杂(并且需要弄清楚如何从结构中存储数组)。我想出了类似查找表的东西,其中每个位置都偏移(以字节为单位)到结构指针中的相应数据。
#define FIT_RECORD_MESG_32_BIT_START 0+3
#define FIT_RECORD_MESG_16_BIT_START 1+3
#define FIT_RECORD_MESG_8_BIT_START 2+3
#define FIT_RECORD_MESG_ARRAY_SIZE_CNT 51//48+3
#define offsetof(s,m) ((size_t)&(((s*)0)->m))
/*Table of offsets to structure*/
const FIT_UINT8 FIT_RECORD_MESG_SIZES[FIT_RECORD_MESG_ARRAY_SIZE_CNT] =
{
FIT_RECORD_MESG_32_BIT_START,
FIT_RECORD_MESG_16_BIT_START,
FIT_RECORD_MESG_8_BIT_START,
offsetof(FIT_RECORD_MESG, timestamp),
offsetof(FIT_RECORD_MESG, speed),
offsetof(FIT_RECORD_MESG, heart_rate),
}
前三个位置是数据类型发生变化的信息。现在通过参考 fields_mesg.fields_array 我可以在结构中找到指向变量的指针并仅打印 1/2/4bytes 或 1/2/4bytes 的数组 - 取决于结构 FIT_(X )_MESG。
/*
local_mesg_number - used by FIT protocol
*array_sizes - pointer to array of offsets from structure address (example. FIT_RECORD_MESG_SIZES)
*mesg_pointer - pointer to data structure (example. FIT_RECORD_MESG)
*fields_mesg - pointer to structure which contains information which messages to send
*/
void WriteMessageRecord(FIT_UINT8 local_mesg_number, FIT_UINT8* array_sizes, const void* mesg_pointer, FIELDS_TO_SEND* fields_mesg, FILE* fp)
{
void *pointer_offset = mesg_pointer; //begining of the structure
uint16_t message_to_send;
uint16_t offset_diference=0;
/*Write begining of the message*/
WriteData(&local_mesg_number, FIT_HDR_SIZE, fp);
/*Go through every message in fields_mesg.fields_array*/
for (uint16_t cnt = 0; cnt < fields_mesg->fields_number;cnt++)
{
message_to_send = fields_mesg->fields_array[cnt];
/*If data type is 1 byte*/
if (message_to_send >= (array_sizes[2]-3))
{
offset_diference = array_sizes[message_to_send + 4] - array_sizes[message_to_send + 3];
/*Check if this isn't array and print as many as array length*/
if (offset_diference > sizeof(uint8_t))
{
for (uint8_t x = 0;x < offset_diference; x++)
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3]+x;
WriteData(((uint8_t*)pointer_offset), 1, fp);
printf("\nARRAY: %d, %d", (*(uint8_t*)pointer_offset), message_to_send);
}
}
else
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3];
WriteData(((uint16_t*)pointer_offset), 1, fp);
printf("\nNOT : %d, %d", (*(uint8_t*)pointer_offset), message_to_send);
}
}
/*If data type is 2 bytes*/
else if (message_to_send >= (array_sizes[1] - 3))
{
offset_diference = (array_sizes[message_to_send + 4] - array_sizes[message_to_send + 3]);
/*Check if this isn't array and print as many as array length*/
if (offset_diference > sizeof(uint16_t))
{
for (uint8_t x = 0;x < offset_diference; x+=2)
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3]+x;
WriteData(((uint16_t*)pointer_offset), 2, fp);
printf("\nARRAY : %d, %d", (*(uint16_t*)pointer_offset), message_to_send);
}
}
else //Print only 2 bytes
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3];
WriteData(((uint16_t*)pointer_offset), 2, fp);
printf("\nNOT : %d, %d", (*(uint16_t*)pointer_offset), message_to_send);
}
}
/*If data type is 4 bytes*/
else if (message_to_send >= (array_sizes[0]-3))
{
offset_diference = (array_sizes[message_to_send + 4] - array_sizes[message_to_send + 3]);
/*Check if this isn't array and print as many as array length*/
if (offset_diference > sizeof(uint32_t))
{
for (uint8_t x = 0;x < offset_diference; x += 4)
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3] + x;
WriteData(((uint32_t*)pointer_offset), 4, fp);
printf("\nARRAY : %d, %d", (*(uint32_t*)pointer_offset), message_to_send);
}
}
else //Print only 4 bytes
{
pointer_offset = ((char*)mesg_pointer) + array_sizes[message_to_send + 3];
WriteData(((uint32_t*)pointer_offset), 4, fp);
printf("\nNOT : %d, %d", (*(uint32_t*)pointer_offset), message_to_send);
}
}
}
}
在主函数中:
// Write message of type: Record
FIT_RECORD_MESG record_mesg;
write_field(FIT_RECORD_MESG_TIMESTAMP); //will be sending timestamp variable
write_field(FIT_RECORD_MESG_ENHANCED_SPEED); //will be sending enhanced_speed variable
record_mesg.timestamp = 5;
record_mesg.enhanced_speed = 6;
WriteMessageDefinitionSelection(0, fit_mesg_defs[FIT_MESG_RECORD], &fields_mesg, fp); //send message definition
WriteMessageRecord(0, FIT_RECORD_MESG_SIZES ,&record_mesg, &fields_mesg, fp); //send message data
clear_fields();
这可行,但我不喜欢它的一些东西:
- 我需要创建额外的偏移数组,这会占用 ROM 中的额外内存。
- 我需要计算每种数据类型的开始位置。
- 对结构的任何更改都会产生错误。
那么.. 有没有更优雅的方式来存储不同数据类型的变量/数组并轻松访问它们?
【问题讨论】:
-
所有这些都是完全错误的。 (1) 如果你需要存储几种不同类型的东西,那么数组不是你想要的。使用结构。 (2)如果需要写struct,就写struct,不要单独写每个字段。扔掉
write_field和它周围的一切,你不需要它。使用FIT_RECORD_MESG record_mesg; ... WriteData(&record_mesg, sizeof(record_mesg), fp); -
你所展示的已经在SDK中实现了。但问题是我不想写整个结构“record_mesg”,因为很多时候我只需要从结构中写几个变量/数组。
-
(*(uint16_t*)pointer_offset)等代码是 strict aliasing violation,因此行为未定义。除了严格的别名违规之外,它还可能违反任何对齐限制并且因此可能是未定义的行为。 -
如果要写变量,就写变量。
WriteData(&record_mesg.timestamp, sizeof(record_mesg.timestamp), fp);。为什么这个特定变量位于结构中很重要? -
@n.'pronouns'm 事实上,在应用程序工作期间要写入的消息/变量的数量可能会发生变化,我只是不想列出
if语句检查要发送的变量。而且我需要为每种大量的消息类型执行此操作:P
标签: arrays c pointers struct structure