我的目标是分配一块内存,然后将其分成不同类型的较小数组。关于我在这里写的代码,我有几个问题:
#include <iostream>
#include <cstdint>
#include <cstdlib>
int main() {
constexpr std::size_t array_count = 5;
constexpr std::size_t elements_size = sizeof(std::uint32_t) + sizeof(std::uint16_t);
void* const pv = std::calloc(array_count, elements_size);
//Partition the memory. p32 array starts at pv, p16 array starts after the 20 byte buffer for the p32 array.
std::uint32_t* const p32 = (std::uint32_t *) pv;
std::uint16_t* const p16 = (std::uint16_t *)((char *) pv + sizeof(std::uint32_t) * array_count);
//Initialize values.
for(std::size_t i = 0; i < array_count; ++i) {
p32[i] = i;
p16[i] = i * 2;
}
//Read them back.
for(std::size_t i = 0; i < array_count; ++i) {
std::cout << p32[i] << std::endl;
std::cout << p16[i] << std::endl;
std::cout << std::endl;
}
std::free(pv);
}
答案 0 :(得分:4)
是的,该计划是UB。当你这样做时:
for(std::size_t i = 0; i < array_count; ++i) {
p32[i] = i;
p16[i] = i * 2;
}
uint32_t
或uint16_t
没有p32
或p16
个对象。 calloc
只给你字节,而不是对象。你不能只有reinterpret_cast
个对象存在。最重要的是,索引仅为数组定义,p32
不指向数组。
为了明确定义,您必须创建一个数组对象。但是,数组的展示新位置为broken,因此您可以手动初始化一堆uint32_t
,如:
auto p32 = reinterpret_cast<uint32_t*>(pv);
for (int i = 0; i < array_count; ++i) {
new (p32+i) uint32_t; // NB: this does no initialization, but it does satisfy
// [intro.object] in actually creating an object
}
然后会遇到一个单独的问题:CWG 2182。现在我们有array_count
uint32_t
个,但我们没有uint32_t[array_count]
所以索引仍然是UB。基本上,完全没有办法用纯粹的标准C ++编写这个。另请参阅my similar question on the topic。
也就是说,在野外执行此操作的代码量非常大,每个实现都允许您执行此操作。
答案 1 :(得分:2)
我只会解决问题的严格别名部分。
C ++标准对malloc
的讨论很少 - 大多数提到它在C中有语义定义。严格阅读C ++标准,没有别名规则违规,因为没有对象别名 - 在C ++中,对象的生命周期在构造之后开始,并且malloc
调用没有构造任何对象。
因此,这是标准未指定的内容(与未定义相反)。
答案 2 :(得分:0)
不,它只是malloc
返回值的正常转换并处理已分配的内存。您可以将这些字节重新解释为您想要的任何数据类型。直到你触摸分配块边界后面的内存。
没有
我知道你只是在询问和尝试,但你应该通常分配内存。
std::uint32_t* const p32 = std::calloc(array_count, sizeof(std::uint32_t));
std::uint16_t* const p16 = std::calloc(array_count, sizeof(std::uint16_t));
答案 3 :(得分:0)
这个程序不安全吗?
在您的示例中,我认为异常完全没有问题,但我想您打算在另一个上下文中使用它。如果您的代码抛出异常,您可能会泄漏一些内存。
你真的需要使用calloc / free吗?在cppcore指南中,您可以找到有关C ++中资源管理的一些指导原则: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#S-resource
例如,R10和R11说“不要明确调用malloc / calloc / free,new / delete”:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-mallocfree
只有当你正在做一些真正需要非常低级别代码的东西时,你需要显式地调用new和delete,但C ++方法是将所有这些调用封装在一个类中,因此该类的用户不需要明确调用new并删除。
但是如果你没有做一些低级别的东西,你考虑使用std :: array&lt;&gt ;?或者std :: vector?