我正在尝试实现FATFS库的接口功能。该实现期望uint8_t *到必须由不同库写入内部SD卡的数据。应使用函数BSP_SD_WriteBlocks(uint32_t *,uint64_t,uint32_t,uint32_t)将数据写入库。 (见下文)
/*
* Write data from a specific sector
*/
int SDMMC::disk_write(const uint8_t *buffer, uint32_t sector, uint32_t count)
{
int res = BSP_SD_WriteBlocks((uint32_t*)buffer, (uint64_t)sector, 512, count);
if (res == MSD_OK) {
return RES_OK;
} else {
return RES_ERROR;
}
}
正如您所看到的,我正在尝试将8位内存地址转换为32位内存地址,并且不要认为这是正确的方法。
不幸的是,我无法更改函数参数,因此disk_write函数必须接受uint8_t *,而BSP_SD_WriteBlocks只接受uint32_t *。
这样做的最佳和禁食方式是什么?
答案 0 :(得分:1)
诀窍就是最初使用一个适当大小的uint32_t
数组(它可以动态分配,见下面我的第一个想法,但不一定)。可以在字节级访问任何对象,因此您可以将uint32_t *
强制转换为uint8_t *
并将其作为字符缓冲区正常处理:您正在访问字节处的原始uint32_t
数组严格别名规则允许的级别。
当你需要uint32_t *
时才退回。由于您只在字节级访问原始数组,因此数组的生命周期尚未结束,uint32_t *
指向有效数组。
较旧且不太好的解决方案
这里的技巧是让malloc
分配缓冲区。 C标准在引用可从C ++程序(*)明确访问的C标准库的部分中说:7.20.3内存管理函数
...如果分配则返回指针 适当地对齐成功,以便可以将其指定给指向任何类型对象的指针 然后用于在分配的空间中访问此类对象或此类对象的数组...
这意味着提供buffer
是malloc
调用的返回值,标准保证它可以安全地转换为任何其他指针类型。
如果不这样做,则存在对齐问题的风险,因为uint32_t
的对齐高于uint8_t
,并且使用错误对齐的指针显式是未定义的行为。
有人可能会说我们违反了严格的别名规则。但任何常见的实现都可以使用它(它会破坏太多的现有代码来拒绝它),并且唯一严格一致的方法是使用缓冲区的完整memcopy ...以完全相同的字节序列结束兼容的对齐!
(*)我知道C和C ++是不同的语言,但C ++标准参考手册在1.2规范性参考文献[intro.refs]中说明
1以下参考文件对于本文件的应用是必不可少的...
- ISO / IEC 9899:1999,编程语言 - C
...
2 ISO / IEC 9899:1999第7条和ISO / IEC 9899:1999 / Cor.1:2001第7条中描述的库 ISO / IEC 9899:1999 / Cor.2:2003的第7条在下文中称为C标准库
...1)具有第18至30条和C.3中所述的资格,C标准库是C ++标准的子集 库。
答案 1 :(得分:1)
这里可能存在多个问题。
const
。在迂腐的世界中,这可能导致不确定的行为。要绝对正确,假设您无法更改BSP_SD_WriteBlocks以获取const指针,则必须复制数据并使用非const指针指向您的副本。奖励是,在制作副本时,您可以解决所有其他问题。缺点是制作副本需要时间和记忆。
在实际的世界中,如果你知道BSP_SD_WriteBlocks永远不会尝试通过该指针进行写入,那么你可能没问题。但这是一个大问题,所以我会使用C ++风格的const_cast<>
来明确表示你是故意这样做的。
在一个迂腐的世界中,从std::uint8_t *
到std::uint32_t *
的强制转换可能不安全,至少不可移植,具体取决于您是否知道原始指针是否已正确对齐,或者您的平台是否允许未对齐访问。请注意,#1中建议的复制可以解决此问题,因为您可以轻松确保临时缓冲区适当对齐。
在实际的世界中,如果您知道源缓冲区将始终适当对齐,这似乎很可能,那么这不是什么大问题。尽管如此,我还是建议使用C ++风格的演员。我还建议断言缓冲区地址是std :: uint32_t大小的倍数。