【问题标题】:C++ a value of type "char" cannot be assigned to an entity of type "char *"C++ 不能将“char”类型的值分配给“char *”类型的实体
【发布时间】:2019-04-15 16:35:36
【问题描述】:

当我尝试初始化一个二维数组时,我从标题中得到了错误。

//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);

//function below
InitializeTable(char***B, int m, int n)
{
for (int i = 0; i < m; i++)
{
    for (int j = 0; j < n; i++)
    {
        B[i][j] = ' '; //getting the error on the equals sign
    }
}

我认为我缺少一些非常简单的东西。谢谢!

【问题讨论】:

  • char*** 将是一个 3 维字符数组。 B[i][j] 是一个字符*。
  • char***B....你确定是三指针吗?
  • char** B; 声明了一个 指向 char 的指针的统一指针 - 在您为指针分配内存之前,它不会指向任何地方,然后为指针指向的内容分配内存。
  • C++ 不是脚本语言。从int m...InitializeTable(&amp;B, ...) 的第一行必须在函数内部(如main)。此外,您将 B 声明为多维数组,但您没有为它分配内存。这将导致内存访问冲突。

标签: c++ multidimensional-array char


【解决方案1】:

您的 “Doahh....” 时刻是:

for (int j = 0; j < n; i++)

为什么在j 循环中使用i++

除了拼写错误,您通常只想将mn 传递给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 = &amp;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. 实际上,您可能想要声明一个向量 &lt;char&gt; 的向量(例如,main() 中的 std::vector&lt;std::vector&lt;char&gt;&gt; 并传递对 InitializeTable 的引用以进行初始化,让 C++ 处理内存管理给你。

【讨论】:

  • 感谢您的精彩解释!
  • 当然,很高兴为您提供帮助。只要你记得,指针并不难交朋友,它们只是一个变量,将地址保存为其他东西作为它的值。通过持有地址(间接级别)从值中删除的每个级别只需取消引用('*')即可删除,char *,一级间接,需要一个取消引用来引用该值,char ** 需要两个等等……你每取一个'&amp;'(地址),增加一层,'*'取走一层。请牢记运算符优先级,您将不会再遇到任何问题。祝你编码顺利。
【解决方案2】:

可以这样想:每组[] 删除一个*。简单案例:char *string = "fred"; string[0] 删除了一个[],这样你就得到了数据(f)

您有***BB[i][j],所以您还有一个* 可以使用...

你没有初始化任何内存也是个问题……

您还应该提供SSCCE

【讨论】:

    【解决方案3】:

    当传递多维数组时,我认为将它们视为引用更容易。因此,在InitializeTable() 中,您可以将 B 声明为对char** 的引用,即。 char**&amp;.

    //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);               // Just pass in B, which will be a reference to char**.
    
    //function below
    InitializeTable(char**&B, int m, int n) // Declare B to be a refernce to char**.
    {
        B = new char * [m];                 // Allocate for first dimension, note these are pointers.
        for (int i = 0; i < m; i++)
        {
            B[i] = new char[n];             // Allocate for second dimension, note these are chars.
            for (int j = 0; j < n; j++)     // Note: you had i++ here.
            {
                B[i][j] = ' ';
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      InitializeTable() 内部,您需要先取消对B 的引用,然后才能对数组进行索引。此外,您没有为数组分配任何内存。试试这样的:

      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; i++)
              {
                  (*B)[i][j] = ' ';
              }
          }
      }
      
      ...
      
      int m = strX.length();
      int n = strY.length();
      
      char** B;
      InitializeTable(&B, m, n);
      
      ...
      
      for (int i = 0; i < m; ++i)
          delete[] B[i];
      delete[] B;
      

      或者,通过引用而不是指针传递B

      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; i++)
              {
                  B[i][j] = ' ';
              }
          }
      }
      
      ...
      
      int m = strX.length();
      int n = strY.length();
      
      char** B;
      InitializeTable(B, m, n);
      
      ...
      
      for (int i = 0; i < m; ++i)
          delete[] B[i];
      delete[] B;
      

      话虽如此,但更好的解决方案是根本不使用原始指针。你应该改用std::vector

      void InitializeTable(std::vector<std::vector<char>> &B, int m, int n)
      {
          B.resize(m);
      
          for (int i = 0; i < m; i++)
          {
              B[i].resize(n);
      
              for (int j = 0; j < n; i++)
              {
                  B[i][j] = ' ';
              }
          }
      
          // alternatively:
          // B = std::vector<std::vector<char>>(m, std::vector<char>(n, ' '));
      }
      
      ...
      
      int m = strX.length();
      int n = strY.length();
      
      std::vector<std::vector<char>> B;
      InitializeTable(B, m, n);
      
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-23
        相关资源
        最近更新 更多