将 C 代码移植到C++,将 void* 从 malloc 转换为所需指针的问题

Porting C code to C++, problem with casting void* from malloc to desired pointer

本文关键字:转换 malloc 问题 指针 代码 C++ void      更新时间:2023-10-16

我目前正在移植我写的一些 C 代码到C++以取乐。我正在努力解决我在 C 中进行的malloc()调用,出于简单原因,hw是常量,但后来与运行时常量交换:

double (*g2)[h][w] = malloc(h * w * sizeof(double));

在C语言中,这是void*的隐式转换,这当然不会随C++而行。

我已经尝试过用reinterpret_cast<double[h][w]>来转换它,但这仍然是一个无效的转换。

我想知道,既然这样可以节省大量工作,我如何在C++中完成这项工作?

作为替代方案,我可能会使用具有间接性的矩阵类:

struct Matrix : std::vector<double> {
unsigned matSize;
std::vector<double*> indirection;
Matrix() : matSize(0) {}
Matrix(unsigned n) : matSize(n) {
resize(n*n);
indirection.resize(n);
for(unsigned i = 0; i < n; ++i) {
indirection[i] = &(*this)[i*n];
}
}
double& operator()(unsigned i, unsigned j) {
return indirection[i][j];
}
const double& operator()(unsigned i, unsigned j) const {
return indirection[i][j];
}
};

移植不仅仅是让它逐行工作,所以:

三:

double (*g2)[h][w] = malloc(h * w * sizeof(double));
...
g2[y][x] = ...;

C++:

std::vector<double> g2(h*w);
...
g2[y+x*h] = ...; // or
g2[y*w+x] = ...;

使用该语法不方便访问元素,因此您可能希望将其包装在一个简单的类中。 示例:

#include <iostream>
#include <iterator>
#include <vector>
class arr2d {
public:
arr2d(size_t h, size_t w) : data_(h * w), w_(w) {}
inline double& operator()(size_t y, size_t x) { 
return data_[y * w_ + x];
}
inline double operator()(size_t y, size_t x) const {
return data_[y * w_ + x];
}
// getting pointer to a row
inline double* operator[](size_t y) {
return &data_[y * w_];
}
inline double const* operator[](size_t y) const {
return &data_[y * w_];
}
inline size_t width() const { return w_; }
private:
std::vector<double> data_;
size_t w_;
};
int main() {
arr2d g2(3, 4);
g2(2, 3) = 3.14159;
// alternative access:
g2[1][2] = 1.23456;
std::cout << g2[2][3] << "n";
double* row = g2[2];
std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
std::cout << "n";
}

输出:

3.14159
0, 0, 0, 3.14159,

非初始化版本可能如下所示:

class arr2d {
public:
arr2d(size_t h, size_t w) : data_(new double[w * h]), w_(w) {}
inline double& operator()(size_t y, size_t x) { return data_[y * w_ + x]; }
inline double operator()(size_t y, size_t x) const { return data_[y * w_ + x]; }
inline double* operator[](size_t y) { return &data_[y * w_]; }
inline double const* operator[](size_t y) const { return &data_[y * w_]; }
inline size_t width() const { return w_; }
private:
std::unique_ptr<double[]> data_;
size_t w_;
};

但请注意,第一个示例中的
std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
会导致未定义的行为。

另请注意,此版本将删除复制构造函数和复制赋值运算符。如果需要,您必须自己实现它们。

非初始化版本的创建时间当然很难被任何初始化版本击败,但对于访问时间,人们可能会认为查找表或您称之为间接寻址的行会加快速度与一次性进行乘法和加法相比。

我的结果:
8x8http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU

它似乎有所不同。4096x4096 矩阵的查找版本更快,但对于两个较小的矩阵,朴素版本更快。您需要比较使用的大小接近您将使用的大小,并检查不同的编译器。在更改编译器时,我有时会得到完全相反的"赢家"。

由于您不介意从std::vector继承或为查找表保留额外的数据,因此这可能是一种选择。它似乎略胜于其他版本。

class arr2d : protected std::vector<double*> {
public:
using std::vector<double*>::operator[]; // "row" accessor from base class
arr2d(size_t h, size_t w) :
std::vector<double*>(h), 
data_(new double[w * h]), 
w_(w),
h_(h)
{
for(size_t y = 0; y < h; ++y)
(*this)[y] = &data_[y * w];
}
inline size_t width() const { return w_; }
inline size_t height() const { return h_; }
private:
std::unique_ptr<double[]> data_;
size_t w_, h_;
};

以下是 Philipp-P (OP:s( 自己对不同 2D 阵列实现的测量结果:

8x8http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg

相同版本的 5 点模板代码的结果:
8x8http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ

C++除非必要,否则不建议手动分配内存。让标准库和模板为您完成工作。

它们非常有用,如果您想进入C++,它们非常适合学习!通过这种方式,您可以节省大量时间并编写一些更好的代码。

例如,此数据类型的用途是什么?如果它适合您的使用,您可以改为考虑使用std::array创建一个 2D 数组:

std::array<std::array<double, w>, h>

如果需要定期调整数组大小,可以改用std::vector。它实际上具有与阵列相同的性能,因为这就是它的全部功能。您可以根据需要reserve()resize()push_back使用1.5倍的递增方案,并且擅长其工作。

编辑:由于大小已知,数组在这里可能更好。从评论中获取建议。

我建议你使用std::vector。如果你想要 2D 数组语法,只需包装它。

#include <iostream>
#include <vector>
class Array2D {
std::vector<double> _v;
size_t _width;
size_t _height;
public:
Array2D(size_t height, size_t width, double initVal = 0.0)
: _v(width * height, initVal),
_width(width),
_height(height)
{}
double* operator[](size_t y) {
return _v.data() + y * _width;
}
};
int main(int, char**) {
size_t rows = 5;
size_t cols = 3;
Array2D a(rows, cols, 0.2);
for (size_t i = 0; i < cols; ++i)
a[4][i] = -0.1 * i;
std::cout << a[4][2] << std::endl; //-0.2
return 0;
}

你可以这样做,这应该在C和C++中都有效:

double *g2 = (double*) malloc(h * w * sizeof(double));

然而,正如其他人所说,这不是一般C++处理这个问题的方法。例如,您应该改用std::vector

#include <vector>
std::vector<double> g2(h * w);

在这两种情况下,您最终都会在单个连续内存块中获得动态分配的 2Ddouble数组。因此,您需要使用g2[(row*w)+col]语法来访问各个元素,其中0 <= row < h0 <= col < w.