将 C 代码移植到 C++,将 void* 从 malloc 转换为所需指针的问题
Porting C code to C++, problem with casting void* from malloc to desired pointer
为了好玩,我目前正在将我编写的一些 C 代码移植到 C++。我正在努力处理我在 C 中进行的 malloc()
调用,出于简单原因,h
和 w
是常量,但后来与运行时常量交换:
double (*g2)[h][w] = malloc(h * w * sizeof(double));
在 C 中,这是一个 void*
的隐式转换,这当然不适用于 C++。
我已经尝试用 reinterpret_cast<double[h][w]>
进行转换,但这仍然是一个无效的转换。
我想知道,我怎样才能在 C++ 中完成这项工作,因为这会节省我很多工作?
作为替代方案,我可能会使用间接矩阵 class:
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];
}
};
移植不仅仅是让它一行一行地工作,所以:
C:
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] = ...;
使用该语法访问元素不方便,因此您可能希望将其包装在一个简单的 class 中。示例:
#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, ", "));
第一个例子会导致未定义的行为。
另请注意,此版本将删除复制构造函数和复制赋值运算符。如果需要,您必须自己实现它们。
非初始化版本的创建时间当然很难与任何初始化版本相提并论,但对于访问时间,人们可能会认为查找 table,或您所说的间接寻址,对于与一次进行乘法和加法相比,这些行会加快速度。
我的结果:
8x8
http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024
http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096
http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU
似乎有所不同。对于 4096x4096 矩阵,查找版本更快,但对于两个较小的矩阵,朴素版本更快。您需要比较使用接近您将要使用的大小,并检查不同的编译器。在更改编译器时,我有时会得到完全相反的“赢家”。
由于您不介意从 std::vector
继承或保留额外数据以供查找 -table,这可能是一个选项。它似乎略胜于其他版本。
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) 自己对不同二维阵列实现的测量:
8x8
http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024
http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096
http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg
相同版本的 5 点模板代码结果:
8x8
http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024
http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096
http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ
在 C++ 中,除非必要,否则不建议手动分配内存。让标准库和模板为您完成工作。
如果您想学习 C++,它们可能非常有用并且非常适合学习!您可以通过这种方式节省大量时间并编写更好的代码。
例如,这个数据类型是做什么用的?如果适合您的使用,您可以考虑使用 std::array
:
创建一个二维数组
std::array<std::array<double, w>, h>
如果您需要定期调整数组大小,可以改用 std::vector
。它实际上具有与数组相同的性能,因为这就是它的全部内容。您可以根据需要 reserve()
或 resize()
并且 push_back
使用 1.5 倍增加的方案并且擅长它的工作。
编辑:由于大小已知,数组在这里可能更好。从评论中采纳建议。
你可以这样做,它应该在 C 和 C++ 中都有效:
double *g2 = (double*) malloc(h * w * sizeof(double));
不过,正如其他人所说,这不是一般在 C++ 中解决此问题的方法。例如,您应该使用 std::vector
代替:
#include <vector>
std::vector<double> g2(h * w);
在这两种情况下,您最终都会在一个连续的内存块中得到动态分配的 double
二维数组。因此,您需要使用 g2[(row*w)+col]
语法来访问各个元素,其中 0 <= row < h
和 0 <= col < w
.
我建议您使用 std::vector。如果你想要二维数组语法,就把它包装起来。
#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++。我正在努力处理我在 C 中进行的 malloc()
调用,出于简单原因,h
和 w
是常量,但后来与运行时常量交换:
double (*g2)[h][w] = malloc(h * w * sizeof(double));
在 C 中,这是一个 void*
的隐式转换,这当然不适用于 C++。
我已经尝试用 reinterpret_cast<double[h][w]>
进行转换,但这仍然是一个无效的转换。
我想知道,我怎样才能在 C++ 中完成这项工作,因为这会节省我很多工作?
作为替代方案,我可能会使用间接矩阵 class:
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];
}
};
移植不仅仅是让它一行一行地工作,所以:
C:
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] = ...;
使用该语法访问元素不方便,因此您可能希望将其包装在一个简单的 class 中。示例:
#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, ", "));
第一个例子会导致未定义的行为。
另请注意,此版本将删除复制构造函数和复制赋值运算符。如果需要,您必须自己实现它们。
非初始化版本的创建时间当然很难与任何初始化版本相提并论,但对于访问时间,人们可能会认为查找 table,或您所说的间接寻址,对于与一次进行乘法和加法相比,这些行会加快速度。
我的结果:
8x8
http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024
http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096
http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU
似乎有所不同。对于 4096x4096 矩阵,查找版本更快,但对于两个较小的矩阵,朴素版本更快。您需要比较使用接近您将要使用的大小,并检查不同的编译器。在更改编译器时,我有时会得到完全相反的“赢家”。
由于您不介意从 std::vector
继承或保留额外数据以供查找 -table,这可能是一个选项。它似乎略胜于其他版本。
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) 自己对不同二维阵列实现的测量:
8x8
http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024
http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096
http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg
相同版本的 5 点模板代码结果:
8x8
http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024
http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096
http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ
在 C++ 中,除非必要,否则不建议手动分配内存。让标准库和模板为您完成工作。
如果您想学习 C++,它们可能非常有用并且非常适合学习!您可以通过这种方式节省大量时间并编写更好的代码。
例如,这个数据类型是做什么用的?如果适合您的使用,您可以考虑使用 std::array
:
std::array<std::array<double, w>, h>
如果您需要定期调整数组大小,可以改用 std::vector
。它实际上具有与数组相同的性能,因为这就是它的全部内容。您可以根据需要 reserve()
或 resize()
并且 push_back
使用 1.5 倍增加的方案并且擅长它的工作。
编辑:由于大小已知,数组在这里可能更好。从评论中采纳建议。
你可以这样做,它应该在 C 和 C++ 中都有效:
double *g2 = (double*) malloc(h * w * sizeof(double));
不过,正如其他人所说,这不是一般在 C++ 中解决此问题的方法。例如,您应该使用 std::vector
代替:
#include <vector>
std::vector<double> g2(h * w);
在这两种情况下,您最终都会在一个连续的内存块中得到动态分配的 double
二维数组。因此,您需要使用 g2[(row*w)+col]
语法来访问各个元素,其中 0 <= row < h
和 0 <= col < w
.
我建议您使用 std::vector。如果你想要二维数组语法,就把它包装起来。
#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;
}