C ++ значение типа «char» не может быть присвоено объекту типа «char *» - PullRequest
0 голосов
/ 13 ноября 2018

Я получаю ошибку из заголовка, когда пытаюсь инициализировать двумерный массив.

//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
    }
}

Я думаю, что-то очень простое, что мне не хватает.Спасибо!

Ответы [ 4 ]

0 голосов
/ 13 ноября 2018

Внутри 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);

...
0 голосов
/ 13 ноября 2018

При прохождении многомерных массивов, я думаю, что их проще воспринимать как ссылки. Таким образом, в InitializeTable() вы можете просто объявить B ссылкой на char**, т.е. char**&.

//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] = ' ';
        }
    }
}
0 голосов
/ 13 ноября 2018

Ваш "Дахах ...." момент:

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

Почему вы используете i++ в цикле j?

Помимо опечатки, вы обычно хотите передать только m и n на InitializeTable, объявить B локально на InitializeTable, выделить m указатели, а затем n символы и назначить начальный адрес для каждого распределения символов для последовательного указателя и возврата B и присвоения возврата обратно в main(). [1] Это предотвращает передачу адреса B в качестве параметра и, таким образом, становление 3-звездочным программистом (не комплимент). Тем не менее, в упражнении есть образовательная цель.

Когда вы объявляете char **B; в main(), B - это унифицированный указатель на указатель на символ . У него есть свой собственный адрес (указатель B), но он нигде не указывает (на самом деле адрес, удерживаемый B, является неопределенным и, скорее всего, каким бы он ни был по адресу B на момент его объявления. Вы не можете используйте B для любых других целей на этом этапе, кроме назначения адреса другого указателя char **, который был правильно инициализирован.

Когда вы передаете адрес B на InitializeTable, например,

    InitializeTable (&B, m, n);

и B получает адрес, вы должны выделить m указатели и назначить начальный адрес для указателей как значение , сохраняемое B (не как адрес 3-звездочного указателя) , Для этого необходимо разыменовать B в InitializeTable. (точно так же, как вы объявляете 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*) и присвоили начальный адрес указателю B в main(), *B в InitializeTable.

Теперь вам нужно выделить n символов для каждого указателя и назначить начальный адрес для каждого блока каждому указателю. Однако, поскольку мы являемся 3-звездочными программистами и имеем один дополнительный уровень косвенности, вместо присвоения B[i], мы должны сначала разыменовать B, но C ++ Precedence операторов вызывает [] для более тесного связывания (имеет более высокий приоритет), чем оператор разыменования '*', поэтому сначала необходимо заключить *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] = ' ';

( примечание: все j с в определении цикла)

Вот и все, что нужно сделать. В целом, вы можете сделать:

#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> (например, std::vector<std::vector<char>> в main() и передать ссылку на InitializeTable для инициализации, позволяя C ++ обрабатывать управление памятью длявы.

0 голосов
/ 13 ноября 2018

Думайте об этом так: каждый набор [] удаляет *.Простой случай: char *string = "fred"; string[0] удаляет [], поэтому вы получаете данные (f)

У вас есть ***B и B[i][j], поэтому у вас еще есть один *, чтобы пойти ...

Тот факт, что вы не инициализировали какую-либо память, также является проблемой ...

Вы также должны указать SSCCE .

...