当前位置: 首页 > 知识库问答 >
问题:

在没有未定义行为的情况下对双打重用浮动缓冲区

殷耀
2023-03-14
void f(float* buffer)
{
  double* d = reinterpret_cast<double*>(buffer);
  // make use of d
  d[i] = 1.;
  // done using d as scratch, start filling the buffer
  buffer[j] = 1.;
}

就我所知,没有简单的方法可以做到这一点:如果我理解正确的话,像这样的reinterprist_cast 会因为类型别名而导致未定义的行为,如果不复制数据和分配额外的空间,就不可能使用memcpy或float/doubleunion,这违背了目的,而且在我的情况下代价很高(在C++中不允许使用union进行类型双关语)。

可以假设浮动缓冲区正确对齐,以便将其用于双打。

共有1个答案

熊朝
2023-03-14

我认为下面的代码是一个有效的方法(它实际上只是关于这个想法的一个小例子):

#include <memory>

void f(float* buffer, std::size_t buffer_size_in_bytes)
{
    double* d = new (buffer)double[buffer_size_in_bytes / sizeof(double)];

    // we have started the lifetime of the doubles.
    // "d" is a new pointer pointing to the first double object in the array.        
    // now you can use "d" as a double buffer for your calculations
    // you are not allowed to access any object through the "buffer" pointer anymore since the floats are "destroyed"       
    d[0] = 1.;
    // do some work here on/with the doubles...


    // conceptually we need to destory the doubles here... but they are trivially destructable

    // now we need to start the lifetime of the floats again
    new (buffer) float[10];  


    // here we are unsure about wether we need to update the "buffer" pointer to 
    // the one returned by the placement new of the floats
    // if it is nessessary, we could return the new float pointer or take the input pointer
    // by reference and update it directly in the function
}

int main()
{
    float* floats = new float[10];
    f(floats, sizeof(float) * 10);
    return 0;
}

重要的是,您只使用从放置新建接收的指针。重要的是要将新的浮动放回原处。即使它是一个没有操作的构造,您也需要重新启动浮动的生存期。

忘记注释中的std::laundreexpert_cast。安置新将为你做这项工作。

    null

为此,我们可以a)通过引用传递浮动指针并更新它,或者b)从函数返回新获得的浮动指针:

a)

void f(float*& buffer, std::size_t buffer_size_in_bytes)
{
    double* d = new (buffer)double[buffer_size_in_bytes / sizeof(double)];    
    // do some work here on/with the doubles...
    buffer = new (buffer) float[10];  
}

b)

float* f(float* buffer, std::size_t buffer_size_in_bytes)
{
    /* same as inital example... */
    return new (buffer) float[10];  
}

int main()
{
    float* floats = new float[10];
    floats = f(floats, sizeof(float) * 10);
    return 0;
}

“如果一个对象属于类或聚合类型,并且它或它的一个子对象是由一个构造函数而不是一个普通的默认构造函数初始化的,则该对象被称为具有非真空初始化。T类型对象的生存期始于:

  • 获得具有正确对齐方式和大小的类型T的存储,并且
  • 如果对象具有非空初始化,则其初始化完成“

所以现在我们可能会争辩说,因为双打是微不足道的,我们需要采取一些行动来让微不足道的物体变得有生命,并改变实际生活的物体吗?我说是的,因为我们从内部获得了浮点的存储,通过双指针访问存储将违反严格的别名。所以我们需要告诉编译器实际类型已经改变。最后第三点的讨论相当有争议。你可以形成自己的观点。你现在手头有所有的信息。

 类似资料:
  • 我一直在做一个倒计时程序,我想到了这个。 在最后的while循环中,如果里面没有print/println语句,倒计时代码就不会执行。为什么?不过,该程序与print语句配合得非常好。

  • 我已经挣扎了几天了。我对Spring Boot还是个新手,喜欢不使用XML配置的想法。 我创建了一个RESTfull应用程序(使用JSON)。我正在按照本教程正确配置身份验证。 可以使用 元素上的entry-point-ref属性设置AuthenticationEntryPoint。 没有提到任何关于如何使用Java配置来实现它的内容。 那么如何在不使用XML的情况下“注册”自己的以防止在使用Fo

  • 文档说这个库运行在GPU上。如果我功能强大的笔记本电脑没有GPU,我还能运行Deeplearning4J吗?

  • 我需要添加多个数组在一起取决于用户的偏好一些数组可能会出现空,我如何确保数组我要Conconat是不是空 这是我失败的代码

  • Perl有一个未定义函数的概念。已声明但未定义的函数。 这个函数现在存在于符号表中,它可以用来解析方法调用。但是为什么这个“特征”会存在呢?在C语言中,这是因为函数是类型检查的,有时您希望在定义之前进行调用(例如解决循环依赖关系)。但是Perl没有这样的特性,所有的函数符号都是在运行时而不是编译时解析的。 > 如果不是原型,为什么它会存在? 为什么在方法解析中使用未定义的子程序?为什么不完全忽略它