您的 “Doahh....” 时刻是:
for (int j = 0; j < n; i++)
为什么在j 循环中使用i++?
除了拼写错误,您通常只想将m 和n 传递给InitializeTable,将B 声明为本地InitializeTable,分配m 指针,然后分配n 字符并分配将每个字符分配的起始地址分配给后续指针并返回 B 并将返回分配回 main()。[1] 这样可以防止传递 B 的地址作为参数,从而成为 3 星程序员(不是恭维)。也就是说,该练习具有教育目的。
当您在 main() 中声明 char **B; 时,B 是一个未初始化的指向 char 指针的指针。它有自己的地址(指针B),但它没有指向任何地方(实际上B 持有的地址是不确定的,很可能只是B 在声明时的地址。你不能此时将B 用于任何其他目的,而不是分配另一个已正确初始化的char ** 指针的地址。
当您将B 的地址传递给InitializeTable 时,例如
InitializeTable (&B, m, n);
而B 收到地址,你必须分配m 指针并将指针的起始地址分配为B 持有的值(而不是3 星指针地址)。为此,您必须在 InitializeTable 中取消引用 B。 (就像你要声明int *a, b = 5;,然后用a = &b 使a 指向b,以更改b 指向的值,你将取消引用和赋值,例如*b = 10;)示例:
void InitializeTable (char ***B, int m, int n)
{
*B = new char*[m];
通过使用new 运算符,您为m 指针(char*)分配了存储空间,并为main() 中的指针B 分配了起始地址,InitializeTable 中的*B。
现在您需要为每个指针分配n 字符,并将每个块的起始地址分配给每个指针。但是,由于我们是 3 星级程序员,并且有一个额外的间接级别要处理,而不是分配给 B[i],我们必须首先取消引用 B -- 但是,C++ 运算符优先级导致[] 比'*' 取消引用运算符绑定更紧密(具有更高的优先级),因此您必须先将*B 括在括号中,例如(*B)[i] 与:
for (int i = 0; i < m; i++) {
(*B)[i] = new char[n];
现在您可以指定空格作为字符来初始化(*B)[i] 中的字符值,例如
for (int j = 0; j < n; j++)
(*B)[i][j] = ' ';
(注意:循环定义中的所有js)
仅此而已。总而言之,您可以这样做:
#include <iostream>
#include <string>
void InitializeTable (char ***B, int m, int n)
{
*B = new char*[m];
for (int i = 0; i < m; i++) {
(*B)[i] = new char[n];
for (int j = 0; j < n; j++)
(*B)[i][j] = ' ';
}
}
int main (void) {
std::string strX = "cats",
strY = "dogs";
//strX and strY are strings
int m = strX.length();
int n = strY.length();
//declare two dynamic 2-Dimensional array of variable length B is m X n
char **B;
InitializeTable (&B, m, n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++)
std::cout << " '" << B[i][j] << "'";
std::cout << '\n';
delete[] B[i]; /* free each block of characters */
}
delete[] B; /* free pointers */
}
(不要忘记释放保存字符的内存以及分配的指针)
使用/输出示例
$ ./bin/threestarc++
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ ./bin/threestarc++
'''''''''
''''''''''
''''''''''
''''''''''
$ valgrind ./bin/threestarc++
==784== Memcheck, a memory error detector
==784== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==784== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==784== Command: ./bin/threestarc++
==784==
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
' ' ' ' ' ' ' '
==784==
==784== HEAP SUMMARY:
==784== in use at exit: 0 bytes in 0 blocks
==784== total heap usage: 8 allocs, 8 frees, 72,810 bytes allocated
==784==
==784== All heap blocks were freed -- no leaks are possible
==784==
==784== For counts of detected and suppressed errors, rerun with: -v
==784== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
脚注:
1. 实际上,您可能想要声明一个向量 <char> 的向量(例如,main() 中的 std::vector<std::vector<char>> 并传递对 InitializeTable 的引用以进行初始化,让 C++ 处理内存管理给你。