热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

这是严格的别名违规吗?任何类型指针都可以作为字符指针的别名吗?

我仍在努力理解严格别名允许和不允许的内容。这个具体的例子是否违反了严格的别名规则?如果不是,为什么?是因为我将新的不同类型放入char*缓冲区吗?templa

我仍在努力理解严格别名允许和不允许的内容。这个具体的例子是否违反了严格的别名规则?如果不是,为什么?是因为我将新的不同类型放入 char* 缓冲区吗?

template
struct Foo
{
struct ControlBlock { unsigned long long numReferences; };
Foo()
{
char* buffer = new char[sizeof(T) + sizeof(ControlBlock)];
// Construct control block
new (buffer) ControlBlock{};
// Construct the T after the control block
this->ptr = buffer + sizeof(ControlBlock);
new (this->ptr) T{};
}
char* ptr;
T* get() {
// Here I cast the char* to T*.
// Is this OK because T* can alias char* or because
// I placement newed a T at char*
return (T*)ptr;
}
};

对于记录,void* 可以为任何其他类型的指针取别名,而任何类型指针都可以为 void* 取别名。char* 可以为任何类型的指针设置别名,但反之是否正确?假设对齐是正确的,任何类型都可以为 char* 别名吗?那么允许以下内容吗?

char* buffer = (char*)malloc(16);
float* pFloat = buffer;
*pFloat = 6; // Can any type pointer alias a char pointer?
// If the above is illegal, then how about:
new (pFloat) float; // Placement new construct a float at pointer
*pFloat = 7; // What about now?

一旦我将 char* 缓冲区指针分配给新分配,为了将其用作浮点缓冲区,我是否需要循环遍历并在每个位置放置新的浮点数?如果我一开始没有将分配分配给 char*,而是先分配给 float*,我就可以立即将其用作浮点缓冲区,对吗?

回答

这是严格的别名违规吗?

是的。

任何类型指针都可以作为字符指针的别名吗?

不。

您可以清洗指针:

T* get() {
return std::launder(reinterpret_cast(ptr)); // OK
}

或者,您可以存储新的展示位置结果:

Foo()
{
...
this->ptr = new (buffer + sizeof(ControlBlock)) T{};
}
T* ptr;
T* get() {
return ptr; // OK
}



我是否需要循环遍历并在每个地方放置一个新的浮点数

自从提案P0593R6被该语言接受后就没有了(C++20)。在此之前,标准要求放置新。你不一定要自己写循环,因为有标准库对于函数模板:std::uninitialized_fill_nuninitialized_default_construct_n等等。另外,你可以放心,一个体面的优化器将编译这样的循环,零个指令。

constexpr std::size_t N = 4;
float* pFloat = static_cast(malloc(N * sizeof(float)));
// OK since P0593R6, C++20
pFloat[0] = 6;
// OK prior to P0593R6, C++20 (to the extent it can be OK)
std::uninitialized_default_construct_n(pFloat, N);
pFloat[0] = 7;
// don't forget
free(pFloat);


PS 不要std::malloc在 C++ 中使用,除非您需要它来与需要它的 C API 交互(即使在 C 中这也是很少见的要求)。我还建议不要重用new char[]缓冲区,因为它对于演示目的来说是不必要的。相反,使用operator ::newwhich 分配存储而不创建对象(即使是微不足道的对象)。或者甚至更好,因为您已经有一个模板,让模板的用户提供他们自己的分配器,使您的模板更普遍有用。





回答

严格别名意味着要取消引用 a T* ptrT该地址必须有一个对象,显然是活着的。实际上,这意味着您不能在两个不兼容的类型之间进行天真位转换,并且编译器可以假设没有两个不兼容类型的指针指向同一位置。

例外是unsigned char,charstd::byte,这意味着您可以将任何对象指针重新解释为这 3 种类型的指针并取消引用它。

(T*)ptr;是有效的,因为ptr那里存在一个T对象。这就是所需要的全部内容,通过它进行了多少次转换,您如何获得该指针*并不重要。当T具有常量成员时还有更多要求,但这与新放置和对象复活有关 -如果您有兴趣,请参阅此答案。

*即使在没有 const 成员的情况下也很重要,可能不确定相关问题。@eerorika 的答案对于建议std::launder或从放置新表达式中分配更正确。

就记录而言,void* 可以为任何其他类型指针取别名,任何类型指针都可以为 void* 取别名。

那不是真的,void不是三种允许的类型之一。但我假设您只是误解了“别名”这个词——严格别名只适用于指针被取消引用时,当然,只要您不取消引用它们,您当然可以自由地拥有指向任何您想要的任何位置的指针。由于void*不能取消引用,这是一个moo点。

解决你的第二个例子

char* buffer = (char*)malloc(16); //OK
// Assigning pointers is always defined the rules only say when
// it is safe to dereference such pointer.
// You are missing a cast here, pointer cannot be casted implicitly in C++, C produces a warning only.
float* pFloat = buffer;
// -> float* pFloat =reinterpret_cast(buffer);
// NOT OK, there is no float at `buffer` - violates strict aliasing.
*pFloat = 6;
// Now there is a float
new (pFloat) float;
// Yes, now it is OK.
*pFloat = 7;






推荐阅读
author-avatar
GXtingker
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有