简体   繁体   English

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

[英]Casting an uint8_t* to uint32_t* in c++

I am trying to implement an interface function of the FATFS library. 我正在尝试实现FATFS库的接口功能。 The implementation expects an uint8_t* to the data that has to be written to an internal SD card by a different library. 该实现期望将uint8_t *传输给必须由其他库写入内部SD卡的数据。 The data should be written to the library using the function BSP_SD_WriteBlocks(uint32_t*, uint64_t, uint32_t, uint32_t). 应使用函数BSP_SD_WriteBlocks(uint32_t *,uint64_t,uint32_t,uint32_t)将数据写入库。 (See Below) (见下文)

/*
 * 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;
    }
}

As you can see I am trying to cast an 8-bit memory address to a 32-bit memory address and don't think that this is the correct way to do so. 如您所见,我正在尝试将8位内存地址转换为32位内存地址,并且不认为这是正确的方法。

Unfortunately, I cannot change the function arguments so the disk_write function must accept uint8_t* and the BSP_SD_WriteBlocks merely accepts uint32_t*. 不幸的是,我无法更改函数参数,因此disk_write函数必须接受uint8_t *,而BSP_SD_WriteBlocks仅接受uint32_t *。

What is the best - and fasted way - to do so? 这样做的最佳方法是什么?

The trick is simply to use initially a uint32_t array of appropriate size (it can be dynamically allocated, see below my first idea, but not necessarily). 技巧很简单,就是最初使用适当大小的uint32_t数组(它可以动态分配,请参见我的第一个想法,但不一定)。 Any object can be accessed at the byte level, so you can cast that uint32_t * to a uint8_t * and process it normally as a character buffer: you are accessing the original uint32_t array at the byte level which is allowed by the strict aliasing rule. 可以在字节级别访问任何对象,因此可以将uint32_t *转换为uint8_t *并将其作为字符缓冲区正常处理:您正在严格的别名规则允许的字节级别访问原始uint32_t数组。

When you need an uint32_t * just cast back. 当您需要uint32_t *只需回退即可。 As you only accessed the original array at the byte level, the lifetime of the array has not ended and the uint32_t * point to a valid array. 由于您仅以字节级别访问原始数组,因此数组的生存期尚未结束,并且uint32_t *指向有效数组。


Older and not so good solution 较旧而不是很好的解决方案

The trick here would be to have the buffer allocated by malloc . 这里的窍门是让缓冲区由malloc分配。 The C standard says in the part referencing the C standard library explicitely accessible from a C++ program (*): 7.20.3 Memory management functions C标准在引用可从C ++程序(*)显式访问的C标准库的部分中说:7.20.3内存管理功能

... The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated... ...如果分配成功,则返回的指针将进行适当对齐,以便可以将其分配给指向任何类型对象的指针,然后将其用于在分配的空间中访问此类对象或此类对象的数组...

That means that provided buffer is the return value of a malloc call, the standard guarantees that it can safely be casted to any other pointer type. 这意味着所提供的buffermalloc调用的返回值,该标准保证可以将其安全地强制转换为任何其他指针类型。

If you do not, you are in risk of an alignment problem because alignment for uint32_t is higher than for uint8_t , and using a bad aligned pointer is explicitly Undefined Behaviour. 否则,您将面临对齐问题的风险,因为uint32_t对齐比uint8_t对齐高,并且使用错误的对齐指针显然是未定义的行为。

One could argue that we are violating the strict aliasing rule here. 有人可能会说我们在这里违反了严格的别名规则。 But any common implementation will be fine with it (it would break too much existing code to reject that) and the only strictly conformant way would be to use a full memcopy of the buffer... to end with the exact same sequence of bytes with a compatible alignment! 但是任何常见的实现都可以(它会破坏太多的现有代码来拒绝它),并且唯一严格一致的方法是使用缓冲区的完整内存复制...以与字节完全相同的字节序列结尾兼容的对齐方式!


(*) I know that C and C++ are different languages but C++ standard reference manual says in 1.2 Normative references [intro.refs] (*)我知道C和C ++是不同的语言,但是C ++标准参考手册在1.2规范性参考[intro.refs]中说

1 The following referenced documents are indispensable for the application of this document... 1以下引用文件对于本文件的应用是必不可少的...
— ISO/IEC 9899:1999, Programming languages — C — ISO / IEC 9899:1999,编程语言— C
... ...
2 The library described in Clause 7 of ISO/IEC 9899:1999 and Clause 7 of ISO/IEC 9899:1999/Cor.1:2001 and Clause 7 of ISO/IEC 9899:1999/Cor.2:2003 is hereinafter called the C standard library.1 2在ISO / IEC 9899:1999的第7条和ISO / IEC 9899:1999 / Cor.1:2001的第7条以及ISO / IEC 9899:1999 / Cor.2:2003的第7条中描述的库在下文中称为C标准库1
... ...

1) With the qualifications noted in Clauses 18 through 30 and in C.3, the C standard library is a subset of the C++ standard library. 1)具有第18条至第30条以及C.3中所述的条件,C标准库是C ++标准库的子集。

There are potentially multiple issues here. 这里可能存在多个问题。

  1. You're casting away the const . 您正在抛弃const

In a pedantic world, this can lead to undefined behavior. 在一个学究的世界中,这可能导致不确定的行为。 To be absolutely correct, assuming you can't change BSP_SD_WriteBlocks to take a const pointer, you would have to make a copy of the data and use a non-const pointer to your copy. 绝对正确,假设您不能将BSP_SD_WriteBlocks更改为采用const指针,则必须制作数据的副本,并使用指向该副本的非const指针。 The bonus is that, in making the copy, you can solve all the other problems. 好处是,在制作副本时,您可以解决所有其他问题。 The downside is that making a copy takes time and memory. 缺点是复制需要时间和内存。

In a practical world, if you know that BSP_SD_WriteBlocks never tries to write through that pointer, you're probably fine. 在实际的世界中,如果您知道BSP_SD_WriteBlocks从未尝试通过该指针进行写操作,那可能很好。 But this is a big deal, so I would use a C++-style const_cast<> to make it clear that you're doing this intentionally. 但这很重要,因此我将使用C ++风格的const_cast<>来表明您是故意这样做的。

  1. Alignment. 对准。

In a pedantic world, the cast from std::uint8_t * to std::uint32_t * may not be safe, at least not portably, depending on whether you know the original pointer is suitably aligned or whether your platform allows unaligned access. 在一个学究的世界中,从std::uint8_t *std::uint32_t *可能并不安全,至少不是可移植的,具体取决于您是否知道原始指针是否正确对齐或平台是否允许未对齐访问。 Note that the copying suggested in #1 can solve this, as you could easily ensure that your temporary buffer is suitably aligned. 请注意,在#1中建议的复制可以解决此问题,因为您可以轻松地确保临时缓冲区正确对齐。

In a practical world, if you know that the source buffer will always be suitably aligned, which seems likely, then this isn't a big deal. 在现实世界中,如果您知道源缓冲区始终会适当对齐(这似乎很可能),那么这没什么大不了的。 Again, though, I'd suggest a C++-style cast. 同样,我建议使用C ++样式的转换。 I'd also recommend an assertion that the buffer address is a multiple of the size of a std::uint32_t. 我还建议断言缓冲区地址是std :: uint32_t大小的倍数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM