将碎片动态分配转换为连续
Convert fragmental dynamic allocation into continuous
我应该创建一个动态分配二维数组的函数,如下所示(对于 n=3):
1
2 1 2
3 2 1 2 3
我编写的代码可以正常工作。但是,我使用了片段动态分配。你能解释一下我如何修改它以使用连续动态分配吗?动态分配对我来说是新的,我有点困惑。能解释一下吗?
#include <cstdlib>
#include <iostream>
#include <new>
#include <stdexcept>
int **MakeTriangle(int n) {
if (n <= 0)
throw std::domain_error("Number of rows must be positive");
int **mat = nullptr;
mat = new int *[n];
for (int i = 0; i < n; i++)
mat[i] = nullptr;
try {
for (int i = 0; i < n; i++)
mat[i] = new int[2 * i + 1];
for (int i = 0; i < n; i++)
for (int j = 0; j < 2 * i + 1; j++)
mat[i][j] = abs(j - i) + 1;
}
catch (std::bad_alloc) {
for (int i = 0; i < n; i++)
delete[] mat[i];
delete[] mat;
throw std::bad_alloc();
}
return mat;
}
int main() {
std::cout << "How many rows you want: ";
int n;
std::cin >> n;
try {
int **mat = nullptr;
mat = MakeTriangle(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < n; i++)
delete[] mat[i];
delete[] mat;
} catch (const std::bad_alloc e) {
std::cout << "Exception: Not enough memory";
} catch (const std::domain_error e) {
std::cout << "Exception: " << e.what();
}
return 0;
}
你基本上有两个选择。要么分配 n * (2 * n + 1)
个元素并浪费其中或多或少的一半,要么分配三角形所需的确切元素数量并在访问它们时小心:
#include <cstddef>
#include <iostream>
int** make_triangle_frag(std::size_t n) {
int** memory = new int*[n];
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
memory[i] = new int[count];
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[i][j] = val;
memory[i][count - j - 1] = val;
}
}
return memory;
}
int* make_triangle_cont_square(std::size_t n) {
auto const rowCount = 2 * n + 1;
int* memory = new int[n * (2 * n + 1)];
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[rowCount * i + j] = val;
memory[rowCount * i + count - j - 1] = val;
}
}
return memory;
}
std::size_t count_elems(std::size_t n) {
std::size_t count{0};
for (std::size_t i = 0; i != n; ++i) count += 2 * n + 1;
return count;
}
int* make_triangle_cont_tri(std::size_t n) {
auto const elemCount = count_elems(n);
int* memory = new int[elemCount];
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[offset + j] = val;
memory[offset + count - j - 1] = val;
}
offset += count;
}
return memory;
}
int main() {
std::size_t const n{10};
{
int** mat = make_triangle_frag(n);
for (std::size_t i = 0; i < n; i++) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < 10; ++i) delete[] mat[i];
delete[] mat;
}
{
// Cons: You waste more or less half the elements you have allocated.
// Pros: It's still easy to access the elements.
int* mat = make_triangle_cont_square(n);
for (std::size_t i = 0; i < n; i++) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i * (2 * n + 1) + j] << " ";
std::cout << std::endl;
}
delete[] mat;
}
{
// Cons: Accessing the triangle elements becomes tricky.
// Pros: You don't allocate more memory than you need.
int* mat = make_triangle_cont_tri(n);
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < count; j++)
std::cout << mat[offset + j] << " ";
std::cout << '\n';
offset += count;
}
delete[] mat;
}
}
输出:
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
请注意,只有在第三种情况下,矩阵元素才会连续排列。这是内存布局的实际样子:
mat[0] -> { 1 }
mat[1] -> { 2, 1, 2 }
mat[2] -> { 3, 2, 1, 2, 3 }
mat { 1, ?, ?, ?, ?, 2, 1, 2, ?, ?, 3, 2, 1, 2, 3 }
mat { 1, 2, 1, 2, 3, 2, 1, 2, 3 }
注2:如果你真的需要一个int**
,你可以试试这个:
int* memory = make_triangle_cont_tri(n);
int** mat = new int*[n];
{
// Init mat
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
mat[i] = memory + offset;
offset += count;
}
}
for (std::size_t i = 0; i < n; ++i) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << '\n';
}
delete[] mat;
delete[] memory;
输出:
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
我应该创建一个动态分配二维数组的函数,如下所示(对于 n=3):
1
2 1 2
3 2 1 2 3
我编写的代码可以正常工作。但是,我使用了片段动态分配。你能解释一下我如何修改它以使用连续动态分配吗?动态分配对我来说是新的,我有点困惑。能解释一下吗?
#include <cstdlib>
#include <iostream>
#include <new>
#include <stdexcept>
int **MakeTriangle(int n) {
if (n <= 0)
throw std::domain_error("Number of rows must be positive");
int **mat = nullptr;
mat = new int *[n];
for (int i = 0; i < n; i++)
mat[i] = nullptr;
try {
for (int i = 0; i < n; i++)
mat[i] = new int[2 * i + 1];
for (int i = 0; i < n; i++)
for (int j = 0; j < 2 * i + 1; j++)
mat[i][j] = abs(j - i) + 1;
}
catch (std::bad_alloc) {
for (int i = 0; i < n; i++)
delete[] mat[i];
delete[] mat;
throw std::bad_alloc();
}
return mat;
}
int main() {
std::cout << "How many rows you want: ";
int n;
std::cin >> n;
try {
int **mat = nullptr;
mat = MakeTriangle(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < n; i++)
delete[] mat[i];
delete[] mat;
} catch (const std::bad_alloc e) {
std::cout << "Exception: Not enough memory";
} catch (const std::domain_error e) {
std::cout << "Exception: " << e.what();
}
return 0;
}
你基本上有两个选择。要么分配 n * (2 * n + 1)
个元素并浪费其中或多或少的一半,要么分配三角形所需的确切元素数量并在访问它们时小心:
#include <cstddef>
#include <iostream>
int** make_triangle_frag(std::size_t n) {
int** memory = new int*[n];
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
memory[i] = new int[count];
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[i][j] = val;
memory[i][count - j - 1] = val;
}
}
return memory;
}
int* make_triangle_cont_square(std::size_t n) {
auto const rowCount = 2 * n + 1;
int* memory = new int[n * (2 * n + 1)];
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[rowCount * i + j] = val;
memory[rowCount * i + count - j - 1] = val;
}
}
return memory;
}
std::size_t count_elems(std::size_t n) {
std::size_t count{0};
for (std::size_t i = 0; i != n; ++i) count += 2 * n + 1;
return count;
}
int* make_triangle_cont_tri(std::size_t n) {
auto const elemCount = count_elems(n);
int* memory = new int[elemCount];
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < i + 1; ++j) {
int const val = i - j + 1;
memory[offset + j] = val;
memory[offset + count - j - 1] = val;
}
offset += count;
}
return memory;
}
int main() {
std::size_t const n{10};
{
int** mat = make_triangle_frag(n);
for (std::size_t i = 0; i < n; i++) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < 10; ++i) delete[] mat[i];
delete[] mat;
}
{
// Cons: You waste more or less half the elements you have allocated.
// Pros: It's still easy to access the elements.
int* mat = make_triangle_cont_square(n);
for (std::size_t i = 0; i < n; i++) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i * (2 * n + 1) + j] << " ";
std::cout << std::endl;
}
delete[] mat;
}
{
// Cons: Accessing the triangle elements becomes tricky.
// Pros: You don't allocate more memory than you need.
int* mat = make_triangle_cont_tri(n);
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
for (std::size_t j = 0; j < count; j++)
std::cout << mat[offset + j] << " ";
std::cout << '\n';
offset += count;
}
delete[] mat;
}
}
输出:
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
请注意,只有在第三种情况下,矩阵元素才会连续排列。这是内存布局的实际样子:
mat[0] -> { 1 }
mat[1] -> { 2, 1, 2 }
mat[2] -> { 3, 2, 1, 2, 3 }
mat { 1, ?, ?, ?, ?, 2, 1, 2, ?, ?, 3, 2, 1, 2, 3 }
mat { 1, 2, 1, 2, 3, 2, 1, 2, 3 }
注2:如果你真的需要一个int**
,你可以试试这个:
int* memory = make_triangle_cont_tri(n);
int** mat = new int*[n];
{
// Init mat
std::size_t offset{0};
for (std::size_t i = 0; i < n; ++i) {
std::size_t const count = 2 * i + 1;
mat[i] = memory + offset;
offset += count;
}
}
for (std::size_t i = 0; i < n; ++i) {
for (std::size_t j = 0; j < 2 * i + 1; j++)
std::cout << mat[i][j] << " ";
std::cout << '\n';
}
delete[] mat;
delete[] memory;
输出:
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10