在 C 中将偏移字节数组转换为 int32 数组
Posted
技术标签:
【中文标题】在 C 中将偏移字节数组转换为 int32 数组【英文标题】:Casting of offsetted byte array to int32 array in C 【发布时间】:2015-10-31 18:02:31 【问题描述】:背景 我正在使用 IAR Embedded Workbench IDE 和在 STM32F091(ARM Cortex-M0 内核)微控制器上运行的工具链开发一个用 C 语言编写的嵌入式应用程序。应用程序将数据写入微控制器嵌入式闪存,其中只能输入 32 位字(也许半字也可以)。
问题描述 数据存储在一个 uint8_t 字节类型数组中,其开头是一些头信息(在这种情况下是来自板载调制解调器的 AT 响应代码),不应将其写入闪存。我想发送一个 uint32_t 指针,指向 uint8_t 缓冲区中实际数据开始的位置。但是如果这个偏移量不是 4 字节对齐的,我的应用程序就会崩溃,因为它试图访问一个未对齐的 uint32_t 类型。
这描述了我正在尝试做的事情(不是真正的代码,只是一个例子):
uint8_t modemResponseBuffer[MAX_MODEM_RESPONSE_SIZE];
/* Get the modem response data (including modem response header data) */
size_t modemResponseSize = GetModemResponseData(modemResponseBuffer);
/* Get the actual data size from the header information */
size_t dataSize = GetActualDataSizeFromModemResponseHeader(modemResponseBuffer);
/* Get the offset to where the actual data starts in the modem response */
size_t modemDataOffset = GetModemResponseDataOffset(modemResponseBuffer);
/* Write the data part of the response to embedded flash memory.
The modemDataOffset can be any number which messes up 4 byte data alignment */
ProgramFlashMemory(DATA_FLASH_STORAGE_ADDRESS, (uint32_t*)&modemResponseBuffer[modemDataoffset],
dataSize);
在ProgramFlashMemory函数内部,FLASH_ProgramWord标准外设库函数被循环调用。
问题 我如何有效地解决这个问题?我正在使用内存有限(32 kb RAM)的系统,因此我不希望将所需内容从 uint8_t 缓冲区复制到 uint32_t 类型的新缓冲区。目前我已经通过循环手动逐字节对齐数据,但这对我来说似乎相当笨拙。但我还没有想出更好的解决方案,我对我可能在这里收到的建议很感兴趣。
另外,如果有人知道,我也想知道为什么应用程序在这种情况下会崩溃。我的核心(或任何核心?)无法处理未对齐的数据类型的原因是什么?
【问题讨论】:
需要表头数据吗? 好吧,我需要它来识别应用程序正在接收的数据类型及其大小。如果我为说明问题而制作的示例不清楚,我很抱歉。 写入闪存后是否需要标头? 在回答我的问题时,我想到了这个问题:您确定ProgramFlashMemory ()
期望字节数 (uint8_t
) 而不是 uint32_t
的数量吗?
"...我的应用程序崩溃了,因为它尝试访问未对齐的 uint32_t 类型。"您对此有何结论?
【参考方案1】:
将ProgramFlashMemory()
更改为void*
,然后在内部将其转换为uint8_t*
,然后将其迭代为一次取四个字节到unit32_t
,然后将其写入闪存。
void*
允许写入任何对象的地址,而无需显式转换。
类似:
int ProgramFlashMemory( uint32_t* addr, void* data, int length )
int byte = 0 ;
int word = 0 ;
while( byte < length )
uint32_t flash_word = 0 ;
// Copy four bytes to word
// Note: little-endian byte order assumed,
// reverse for big-endian.
// If end is not aligned, fill with 0xff.
for( b = 0; b < 4; b++ )
flash_word |= byte < length ?
(uint8_t*)data[byte] << (b<<3) :
0xFF ;
byte++ ;
ProgramFlashWord( addr[word], flash_word ) ;
word++ ;
// Return bytes written - may be linger than `length` by up-to
// three for end-alignment.
return byte ;
您可能希望保留原始的ProgramFlashMemory()
以实现高效的对齐写入,在这种情况下可能是ProgramFlashMemoryUnaligned()
。请注意,您需要注意的不仅仅是对齐方式,而且长度不一定能被 4 整除。
【讨论】:
这看起来很棒,下周将尝试制作我自己的 ProgramFlashMemoryUnaligned。甚至还可以创建一个 CheckIfAligned 函数。 动态检查对齐和使用不同函数执行编程的一个潜在问题是,您的应用程序性能可能会发生不确定的变化,这在硬实时应用程序中可能是不可取的。我的建议是对始终对齐的对象使用对齐操作,而当它们可能未对齐时使用未对齐操作 - 只是为了使性能具有确定性。 ... 也就是说,在 STM32 上它主要是学术性的 - 在 STM32 上编程片上闪存会停止总线,因此会在相当长的时间内阻止指令获取(STM32F1 上长达 40 毫秒,STM32F2 上长达 800 毫秒) !),因此 STM32 上的片上闪存编程已经是不确定的,不适合硬实时应用。你应该非常小心那个“功能”。 感谢您的提醒,但我已经收集了很多关于闪存编程和内核停止的信息。它本身不是一个实时系统。我曾考虑修改标准外设库代码以进行闪存编程并从 RAM 运行它以避免此问题,但目前没有必要。 对于这里的“硬实时”的含义似乎存在一些误解。根据定义,具有硬实时意味着错过最后期限被认为是整个系统故障。只要您的系统级设计能够每次考虑最坏情况的编程时间,您仍然可以在技术上在硬实时系统中使用此功能。除了在运行时自行更新您自己的闪存之外,对于按照硬实时规范开发的任何系统几乎总是禁止使用的,并且当不运行时,可能会出现例外情况。【参考方案2】:这假设在写入有效负载数据后不再需要标头数据。
为了确保缓冲区的对齐方式正确,您可能需要像这样声明它:
uint32_t modemResponseBuffer[(MAX_MODEM_RESPONSE_SIZE * sizeof (uint8_t) / sizeof (uint32_t)) + 1];
只需在调用 writer 函数之前将有效负载数据移动到缓冲区的开头:
memmove(modemResponseBuffer, modemResponseBuffer + modemDataoffset, dataSize);
请注意,memcpy()
在此处不起作用,因为目标和源重叠。
然后这样调用作者:
ProgramFlashMemory(DATA_FLASH_STORAGE_ADDRESS, modemResponseBuffer, dataSize);
【讨论】:
感谢您的建议,使用 memmove 可能会起作用。但是,由于内存重叠(如您所述), memmove 将创建数据的临时副本。这是不可取的,因为数据可能高达 2048 字节。 @Stenis:然后使用memcpy()
使用循环,每次迭代只复制与以前使用的标头一样多的字节。
是的,我目前有一个类似的解决方案,但它只是按字节复制。考虑到您建议的修改,这可能是最好的方法。【参考方案3】:
ARM Cortex-M0 内核在未对齐访问时崩溃的原因是因为that's what it's designed to do。采用硬故障异常实际上是对一些旧内核的改进,这些内核会错误地访问该值,然后继续使用损坏的值执行。一些较新的 ARM 内核对未对齐访问的硬件支持有限。
这里有一些建议。
重新设计ProgramFlashMemory()
,使其接受uint8_t*
而不是uint32_t*
。为了对字进行编程,它应该将缓冲区中的各个字节复制到具有正确对齐方式的局部变量中。然后将局部变量copy写入flash。
重新设计GetModemResponseData()
,以便在从流中读取标头时解析标头。在从流中读取数据之前,它将确定标头的长度和数据的起点。当流位于数据的开头时,它应该开始将数据复制到与标头分开并正确对齐的新缓冲区中。
【讨论】:
感谢您的建议,通过对标头和数据使用单独的缓冲区来保持较低的内存使用率(不是最低限度,但足够低)肯定是一个可行的选择。 如果其他人对数据对齐和 CPU 感兴趣,我在 *** 上找到了专门的问题:***.com/questions/3025125/cpu-and-data-alignment以上是关于在 C 中将偏移字节数组转换为 int32 数组的主要内容,如果未能解决你的问题,请参考以下文章