在 C++ 中获取指向复数向量的实部和虚部的指针

Posted

技术标签:

【中文标题】在 C++ 中获取指向复数向量的实部和虚部的指针【英文标题】:Getting pointers to the real and imaginary parts of a complex vector in C++ 【发布时间】:2014-06-15 12:56:18 【问题描述】:

使用标准 C++ 复数和向量库,我定义了一个复数向量。现在,我想获得指向包含这个复数向量的实部和虚部的向量的指针(double * 类型)。以下解决方案有效,但由于内存使用量增加了一倍,因此不优雅且浪费;

using namespace std;
typedef complex<double> cmp;
.
.
int i,m=10;
vector<cmp> C(m);
//Do something to populate the vector C
vector<double> R(m), I(m);
for(i=0; i<m; i++)
 R[i] = C[i].real();
 I[i] = C[i].imag();

double * r = &R[0];
double * i = &I[0];

【问题讨论】:

为什么要指针? 如果你必须有一个双精度数组,你就无法避免加倍的内存使用,因为实部和虚部在内存中并不相邻。 没有安全的方法可以做到这一点。双原语封装在complex&lt;double&gt; 对象中。你试图在错误的范围内解决更高层次的问题。 @Jefffrey 我需要将指针传递给我无法控制的第 3 方代码。 【参考方案1】:

根据 C++ 标准

If z is an lvalue expression of type cv std::complex<T> then:
— the expression reinterpret_cast<cv T(&)[2]>(z) shall be well-formed,
— reinterpret_cast<cv T(&)[2]>(z)[0] shall designate the real part of z, and
— reinterpret_cast<cv T(&)[2]>(z)[1] shall designate the imaginary part of z.
Moreover, if a is an expression of type cv std::complex<T>* and the expression a[i] is well-defined
for an integer expression i, then:
— reinterpret_cast<cv T*>(a)[2*i] shall designate the real part of a[i], and
— reinterpret_cast<cv T*>(a)[2*i + 1] shall designate the imaginary part of a[i].

所以你可以简单地写

using namespace std;
typedef complex<double> cmp;
.
.
int i,m=10;
vector<cmp> C(m);
//Do something to populate the vector C

double * r = &reinterpret_cast<double(&)[2]>( C[0] )[0];
double * i = &reinterpret_cast<double(&)[2]>( C[0] )[1];

这是一个例子

#include <iostream>
#include <complex>
#include <vector>

int main() 

    std::vector<std::complex<double>> v( 1,  1.1, 2.2  );

    double * r = &reinterpret_cast<double(&)[2]>( v[0] )[0];
    double * i = &reinterpret_cast<double(&)[2]>( v[0] )[1];

    std::cout << *r << '\t' << *i << std::endl;

    return 0;

输出是

1.1 2.2

【讨论】:

太好了,感谢@Vlad 提供有用的答案和示例代码:)。 这个答案是不正确的——至少对于具有多个元素的向量来说是这样。 vector&lt;complex&gt; 对象的内存结构通常为[RIRIRIRIRI...],这意味着您无法获得指向实部或虚部的指针。我说通常,因为正式地说,这取决于编译器的实现。但实际上,内存通常是连续的。【参考方案2】:

(C++03) 标准没有定义 std::complex&lt;double&gt; 的内部结构如何,但通常它由 2 个双精度数组成,实部在虚部之前。因此,给定一个std::complex&lt;double&gt; 的数组(或std::vector),您无法获得一个指向所有实部数组的指针,以及另一个指向所有虚部数组的指针:实部和虚部是交错的。如果您确实需要拆分它们,则必须复制所有元素(就像您已经这样做的那样)。

但是你为什么首先要把它们分开呢?将它们传递给某个库的一些例程?也许那个库也支持交错格式?在这种情况下,您可以使用reinterpret_cast&lt;double*&gt;(&amp;C[0])。请注意,这是非标准的,但它似乎在大多数情况下都有效。有关详细信息,请参阅广泛使用的 fftw-library 的 documentation,推荐使用此方法。

如果考虑性能,您应该从一开始就将实部和虚部分开,而不是先构造std::complex&lt;double&gt; 向量。

【讨论】:

感谢您的回答并提醒我注意 fftw 库中的相同问题。可以想象,我需要进行这种拆分,因为某些第三方库(阅读:Matlab)希望将它们的复杂数据放在单独的真实和虚构容器中。 @UdX 我认为您误读了我的回答。 fftw 库需要 interleaved 格式。并且因为 std::complex 是二进制兼容的(在 C++11 中)到 double[2] (这是 fftw 所期望的),所以您可以进行强制转换。当 Matlab 需要拆分格式时,您必须从一开始就将它们分开处理,或者进行额外的复制操作(如您的第一篇文章中所述)。 有趣。帮助我更好地理解这一点:假设我有一个复数向量,其数据存储在以 1,3,5,.. 开头的内存位置中(假设存储了 1 个双精度数/位置)。现在,如果我有一个双精度向量,则内存位置将类似于 1,2,3,... 那么,在@Vlad 的答案的情况下,重新解释演员究竟在做什么?看起来r 指向 1,3,5,.. 和 i 指向 2,4,6,.. 但由于这些是 double * 类型,对它们的迭代应该按以下步骤完成1,而不是 2。然而,在 @Vlad 的回答中,迭代 ri 似乎有效。 :-? 不,从弗拉德的回答中取 ri,你有以下内容:r[0] 给出第一个数字的实部,r[1] 给出 虚数 第一个数字的一​​部分,r[2] 给出第二个数字的实部,r[3] 给出第三个数字的虚部。等等。对于ii[0] 给出第一个数字的虚部,i[1] 给出第二个数字的实部,i[2] 给出第二个数字的虚部。等等。因此,ir 相比偏移了 1 个元素。弗拉德并没有真正回答你的问题。 谢谢@Gugi,我现在明白了。我的误解源于没有仔细检查 Vlad 的方法应用于大小 > 1 的向量。我现在已将正确答案更新为您的答案。

以上是关于在 C++ 中获取指向复数向量的实部和虚部的指针的主要内容,如果未能解决你的问题,请参考以下文章

GDB - 访问复数的实部和虚部

FFTW 和 OpenCV 的 C++ 接口,Mat 输出中的实部和虚部

如何在C中分配复变量的实部和虚部

python二级练习和考试复习(将复数2.3103-1.3410-3j赋值给变量A,并分别提取A的实部和虚部。)

python里的复数complex

定义一个复数类,并实现以下复数类的方法:构造方法、得到实部、得到虚部、设置实部、设置虚部、复数的加