热门标签 | 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;






推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
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社区 版权所有