在c ++中将uint8_t *转换为uint32_t *

时间:2017-01-27 15:26:41

标签: c++ pointers casting fatfs

我正在尝试实现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 *。

这样做的最佳和禁食方式是什么?

2 个答案:

答案 0 :(得分:1)

诀窍就是最初使用一个适当大小的uint32_t数组(它可以动态分配,见下面我的第一个想法,但不一定)。可以在字节级访问任何对象,因此您可以将uint32_t *强制转换为uint8_t *并将其作为字符缓冲区正常处理:您正在访问字节处的原始uint32_t数组严格别名规则允许的级别。

当你需要uint32_t *时才退回。由于您只在字节级访问原始数组,因此数组的生命周期尚未结束,uint32_t *指向有效数组。

较旧且不太好的解决方案

这里的技巧是让malloc分配缓冲区。 C标准在引用可从C ++程序(*)明确访问的C标准库的部分中说:7.20.3内存管理函数

  

...如果分配则返回指针   适当地对齐成功,以便可以将其指定给指向任何类型对象的指针   然后用于在分配的空间中访问此类对象或此类对象的数组...

这意味着提供buffermalloc调用的返回值,标准保证它可以安全地转换为任何其他指针类型。

如果不这样做,则存在对齐问题的风险,因为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)

这里可能存在多个问题。

  1. 你正在抛弃const
  2. 在迂腐的世界中,这可能导致不确定的行为。要绝对正确,假设您无法更改BSP_SD_WriteBlocks以获取const指针,则必须复制数据并使用非const指针指向您的副本。奖励是,在制作副本时,您可以解决所有其他问题。缺点是制作副本需要时间和记忆。

    在实际的世界中,如果你知道BSP_SD_WriteBlocks永远不会尝试通过该指针进行写入,那么你可能没问题。但这是一个大问题,所以我会使用C ++风格的const_cast<>来明确表示你是故意这样做的。

    1. 对齐。
    2. 在一个迂腐的世界中,从std::uint8_t *std::uint32_t *的强制转换可能不安全,至少不可移植,具体取决于您是否知道原始指针是否已正确对齐,或者您的平台是否允许未对齐访问。请注意,#1中建议的复制可以解决此问题,因为您可以轻松确保临时缓冲区适当对齐。

      在实际的世界中,如果您知道源缓冲区将始终适当对齐,这似乎很可能,那么这不是什么大问题。尽管如此,我还是建议使用C ++风格的演员。我还建议断言缓冲区地址是std :: uint32_t大小的倍数。