c++ 指向内存的指针可以用作初始化的平凡类型吗?

h4cxqtbf  于 2022-12-01  发布在  其他
关注(0)|答案(1)|浏览(187)

在没有初始化的情况下使用平凡类型是否是未定义的行为?

void* mem = malloc(sizeof(uint64_t)*100);
void* num_mem = mem + sizeof(uint64_t)*31;
//does the lifetime of uint64_t starts here:
uint64_t* mynum = reinterpret_cast<uint64_t*>(num_mem); //?
*mynum = 5; //is it UB?
std::cout << *mynum << std::endl; //is it UB?
free(mem);

我发现调用trivial/POD/aggregate类型的析构函数是不必要的,但是在trivial/POD/aggregate类型的生存期开始时找不到相同的析构函数。我是否必须调用new(num_mem) uint64_t;而不是reinterpret_cast ..
如果它是一个POD或聚合对象,而没有构造函数,行为是否会改变?

cngwdvgl

cngwdvgl1#

下面的所有内容都遵循C20的规则,尽管隐式对象创建也被认为是针对早期版本的缺陷报告。在C17之前,指针值的含义非常不同,因此答案和注解中的讨论可能不适用。所有这些也都严格地与标准保证有关。当然,编译器在实践中可能允许更多。
是的,如果类型和它的所有子对象的类型都是implicit-lifetime types,这是一个比trivial或POD弱的要求。它确实适用于聚合类型,但不一定适用于聚合类型的子对象,在这种情况下,它们仍然需要显式地进行new处理(例如,考虑struct A { std::string s; };的成员)。
您还需要确保内存的大小和对齐方式是合适的。std::malloc返回的内存对齐至少与std::max_align_t一样严格,因此它适用于任何标量类型,但不适用于过度对齐类型。
另外,std::malloc是几个专门指定用来隐式创建对象并返回指向对象的指针的函数之一。它通常不适用于任意内存。例如,如果您使用内存位置uint64_t,则隐式创建的对象是uint64_t。之后强制转换为其他类型将导致别名冲突。
细节相当复杂,而且很容易出错。使用new显式创建对象(也不需要初始化为值),然后使用new返回的指针访问该对象要安全得多。
mem + sizeof(uint64_t)*31也是一个GNU扩展。在标准C++中,不可能对void*指针执行指针运算。您需要先转换为元素类型,然后执行运算,假设您只在内存中存储相同的类型。否则,它会变得有点复杂。
(Also,您遗漏了malloc传回值的null指标检查。)

相关问题